def _dup_ff_trivial_gcd(f, g, K): """Handle trivial cases in GCD algorithm over a field. """ if not (f or g): return [], [], [] elif not f: return dup_monic(g, K), [], [dup_LC(g, K)] elif not g: return dup_monic(f, K), [dup_LC(f, K)], []
def dup_sqf_list(f, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = 2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16 >>> R.dup_sqf_list(f) (2, [(x + 1, 2), (x + 2, 3)]) >>> R.dup_sqf_list(f, all=True) (2, [(1, 1), (x + 1, 2), (x + 2, 3)]) """ if K.is_FiniteField: return dup_gf_sqf_list(f, K, all=all) if K.has_Field: coeff = dup_LC(f, K) f = dup_monic(f, K) else: coeff, f = dup_primitive(f, K) if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) coeff = -coeff if dup_degree(f) <= 0: return coeff, [] result, i = [], 1 h = dup_diff(f, 1, K) g, p, q = dup_inner_gcd(f, h, K) while True: d = dup_diff(p, 1, K) h = dup_sub(q, d, K) if not h: result.append((p, i)) break g, p, q = dup_inner_gcd(p, h, K) if all or dup_degree(g) > 0: result.append((g, i)) i += 1 return coeff, result
def dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K): """Wang/EEZ: Compute correct leading coefficients. """ C, J, v = [], [0] * len(E), u - 1 for h in H: c = dmp_one(v, K) d = dup_LC(h, K) * cs for i in reversed(range(len(E))): k, e, (t, _) = 0, E[i], T[i] while not (d % e): d, k = d // e, k + 1 if k != 0: c, J[i] = dmp_mul(c, dmp_pow(t, k, v, K), v, K), 1 C.append(c) if any(not j for j in J): raise ExtraneousFactors # pragma: no cover CC, HH = [], [] for c, h in zip(C, H): d = dmp_eval_tail(c, A, v, K) lc = dup_LC(h, K) if K.is_one(cs): cc = lc // d else: g = K.gcd(lc, d) d, cc = d // g, lc // g h, cs = dup_mul_ground(h, d, K), cs // d c = dmp_mul_ground(c, cc, v, K) CC.append(c) HH.append(h) if K.is_one(cs): return f, HH, CC CCC, HHH = [], [] for c, h in zip(CC, HH): CCC.append(dmp_mul_ground(c, cs, v, K)) HHH.append(dmp_mul_ground(h, cs, 0, K)) f = dmp_mul_ground(f, cs**(len(H) - 1), u, K) return f, HHH, CCC
def _dup_rr_trivial_gcd(f, g, K): """Handle trivial cases in GCD algorithm over a ring. """ if not (f or g): return [], [], [] elif not f: if K.is_nonnegative(dup_LC(g, K)): return g, [], [K.one] else: return dup_neg(g, K), [], [-K.one] elif not g: if K.is_nonnegative(dup_LC(f, K)): return f, [K.one], [] else: return dup_neg(f, K), [-K.one], []
def dup_pdiv(f, g, K): """ Polynomial pseudo-division in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_pdiv(x**2 + 1, 2*x - 4) (2*x + 4, 20) """ df = dup_degree(f) dg = dup_degree(g) q, r, dr = [], f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r N = df - dg + 1 lc_g = dup_LC(g, K) while True: lc_r = dup_LC(r, K) j, N = dr - dg, N - 1 Q = dup_mul_ground(q, lc_g, K) q = dup_add_term(Q, lc_r, j, K) R = dup_mul_ground(r, lc_g, K) G = dup_mul_term(g, lc_r, j, K) r = dup_sub(R, G, K) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) c = lc_g**N q = dup_mul_ground(q, c, K) r = dup_mul_ground(r, c, K) return q, r
def dup_zz_factor_sqf(f, K): """Factor square-free (non-primitive) polyomials in `Z[x]`. """ cont, g = dup_primitive(f, K) n = dup_degree(g) if dup_LC(g, K) < 0: cont, g = -cont, dup_neg(g, K) if n <= 0: return cont, [] elif n == 1: return cont, [g] if query('USE_IRREDUCIBLE_IN_FACTOR'): if dup_zz_irreducible_p(g, K): return cont, [g] factors = None if query('USE_CYCLOTOMIC_FACTOR'): factors = dup_zz_cyclotomic_factor(g, K) if factors is None: factors = dup_zz_zassenhaus(g, K) return cont, _sort_factors(factors, multiple=False)
def _dup_right_decompose(f, s, K): """Helper function for :func:`_dup_decompose`.""" n = len(f) - 1 lc = dup_LC(f, K) f = dup_to_raw_dict(f) g = {s: K.one} r = n // s for i in range(1, s): coeff = K.zero for j in range(0, i): if not n + j - i in f: continue if not s - j in g: continue fc, gc = f[n + j - i], g[s - j] coeff += (i - r * j) * fc * gc g[s - i] = K.quo(coeff, i * r * lc) return dup_from_raw_dict(g, K)
def dup_half_gcdex(f, g, K): """ Half extended Euclidean algorithm in `F[x]`. Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. Examples ======== >>> from diofant.polys import ring, QQ >>> R, x = ring("x", QQ) >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 >>> g = x**3 + x**2 - 4*x - 4 >>> R.dup_half_gcdex(f, g) (-1/5*x + 3/5, x + 1) """ if not K.has_Field: raise DomainError("can't compute half extended GCD over %s" % K) a, b = [K.one], [] while g: q, r = dup_div(f, g, K) f, g = g, r a, b = b, dup_sub_mul(a, q, b, K) a = dup_quo_ground(a, dup_LC(f, K), K) f = dup_monic(f, K) return a, f
def dup_monic(f, K): """ Divide all coefficients by ``LC(f)`` in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_monic(3*x**2 + 6*x + 9) x**2 + 2*x + 3 >>> R, x = ring("x", QQ) >>> R.dup_monic(3*x**2 + 4*x + 2) x**2 + 4/3*x + 2/3 """ if not f: return f lc = dup_LC(f, K) if K.is_one(lc): return f else: return dup_exquo_ground(f, lc, K)
def dup_ext_factor(f, K): """Factor univariate polynomials over algebraic number fields. """ n, lc = dup_degree(f), dup_LC(f, K) f = dup_monic(f, K) if n <= 0: return lc, [] if n == 1: return lc, [(f, 1)] f, F = dup_sqf_part(f, K), f s, g, r = dup_sqf_norm(f, K) factors = dup_factor_list_include(r, K.domain) if len(factors) == 1: return lc, [(f, n // dup_degree(f))] H = s * K.unit for i, (factor, _) in enumerate(factors): h = dup_convert(factor, K.domain, K) h, _, g = dup_inner_gcd(h, g, K) h = dup_shift(h, H, K) factors[i] = h factors = dup_trial_division(F, factors, K) return lc, factors
def dup_sqf_part(f, K): """ Returns square-free part of a polynomial in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sqf_part(x**3 - 3*x - 2) x**2 - x - 2 """ if K.is_FiniteField: return dup_gf_sqf_part(f, K) if not f: return f if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) gcd = dup_gcd(f, dup_diff(f, 1, K), K) sqf = dup_quo(f, gcd, K) if K.has_Field: return dup_monic(sqf, K) else: return dup_primitive(sqf, K)[1]
def dup_compose(f, g, K): """ Evaluate functional composition ``f(g)`` in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_compose(x**2 + x, x - 1) x**2 - x """ if len(g) <= 1: return dup_strip([dup_eval(f, dup_LC(g, K), K)]) if not f: return [] h = [f[0]] for c in f[1:]: h = dup_mul(h, g, K) h = dup_add_term(h, c, 0, K) return h
def dup_zz_mignotte_bound(f, K): """Mignotte bound for univariate polynomials in `K[x]`. """ a = dup_max_norm(f, K) b = abs(dup_LC(f, K)) n = dup_degree(f) return K.sqrt(K(n + 1)) * 2**n * a * b
def dup_rr_div(f, g, K): """ Univariate division with remainder over a ring. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rr_div(x**2 + 1, 2*x - 4) (0, x**2 + 1) """ df = dup_degree(f) dg = dup_degree(g) q, r, dr = [], f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r lc_g = dup_LC(g, K) while True: lc_r = dup_LC(r, K) if lc_r % lc_g: break c = K.exquo(lc_r, lc_g) j = dr - dg q = dup_add_term(q, c, j, K) h = dup_mul_term(g, c, j, K) r = dup_sub(r, h, K) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return q, r
def dup_zz_hensel_lift(p, f, f_list, l, K): """ Multifactor Hensel lifting in `Z[x]`. 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 References ========== .. [1] [Gathen99]_ """ r = len(f_list) lc = dup_LC(f, K) if r == 1: F = dup_mul_ground(f, K.gcdex(lc, p**l)[0], K) return [dup_trunc(F, p**l, K)] m = p k = 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, K) 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, K) s, t, _ = gf_gcdex(g, h, p, K) 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 = dup_zz_hensel_step(m, f, g, h, s, t, K), m**2 return dup_zz_hensel_lift(p, g, f_list[:k], l, K) \ + dup_zz_hensel_lift(p, h, f_list[k:], l, K)
def dup_prem(f, g, K): """ Polynomial pseudo-remainder in ``K[x]``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_prem(x**2 + 1, 2*x - 4) 20 """ df = dup_degree(f) dg = dup_degree(g) r, dr = f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return r N = df - dg + 1 lc_g = dup_LC(g, K) while True: lc_r = dup_LC(r, K) j, N = dr - dg, N - 1 R = dup_mul_ground(r, lc_g, K) G = dup_mul_term(g, lc_r, j, K) r = dup_sub(R, G, K) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return dup_mul_ground(r, lc_g**N, K)
def dup_zz_irreducible_p(f, K): """Test irreducibility using Eisenstein's criterion. """ lc = dup_LC(f, K) tc = dup_TC(f, K) e_fc = dup_content(f[1:], K) if e_fc: e_ff = factorint(int(e_fc)) for p in e_ff.keys(): if (lc % p) and (tc % p**2): return True
def _dup_left_decompose(f, h, K): """Helper function for :func:`_dup_decompose`.""" g, i = {}, 0 while f: q, r = dup_div(f, h, K) if dup_degree(r) > 0: return else: g[i] = dup_LC(r, K) f, i = q, i + 1 return dup_from_raw_dict(g, K)
def dup_qq_heu_gcd(f, g, K0): """ Heuristic polynomial GCD in `Q[x]`. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. Examples ======== >>> from diofant.polys import ring, QQ >>> R, x = ring("x", QQ) >>> f = QQ(1,2)*x**2 + QQ(7,4)*x + QQ(3,2) >>> g = QQ(1,2)*x**2 + x >>> R.dup_qq_heu_gcd(f, g) (x + 2, 1/2*x + 3/4, 1/2*x) """ result = _dup_ff_trivial_gcd(f, g, K0) if result is not None: return result K1 = K0.get_ring() cf, f = dup_clear_denoms(f, K0, K1) cg, g = dup_clear_denoms(g, K0, K1) f = dup_convert(f, K0, K1) g = dup_convert(g, K0, K1) h, cff, cfg = dup_zz_heu_gcd(f, g, K1) h = dup_convert(h, K1, K0) c = dup_LC(h, K0) h = dup_monic(h, K0) cff = dup_convert(cff, K1, K0) cfg = dup_convert(cfg, K1, K0) cff = dup_mul_ground(cff, K0.quo(c, cf), K0) cfg = dup_mul_ground(cfg, K0.quo(c, cg), K0) return h, cff, cfg
def dup_zz_cyclotomic_factor(f, K): """ Efficiently factor polynomials `x**n - 1` and `x**n + 1` in `Z[x]`. Given a univariate polynomial `f` in `Z[x]` returns a list of factors of `f`, provided that `f` is in the form `x**n - 1` or `x**n + 1` for `n >= 1`. Otherwise returns None. Factorization is performed using using cyclotomic decomposition of `f`, which makes this method much faster that any other direct factorization approach (e.g. Zassenhaus's). References ========== .. [1] [Weisstein09]_ """ lc_f, tc_f = dup_LC(f, K), dup_TC(f, K) if dup_degree(f) <= 0: return if lc_f != 1 or tc_f not in [-1, 1]: return if any(bool(cf) for cf in f[1:-1]): return n = dup_degree(f) F = _dup_cyclotomic_decompose(n, K) if not K.is_one(tc_f): return F else: H = [] for h in _dup_cyclotomic_decompose(2 * n, K): if h not in F: H.append(h) return H
def dup_rr_prs_gcd(f, g, K): """ Computes polynomial GCD using subresultants over a ring. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rr_prs_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 2) """ result = _dup_rr_trivial_gcd(f, g, K) if result is not None: return result fc, F = dup_primitive(f, K) gc, G = dup_primitive(g, K) c = K.gcd(fc, gc) h = dup_subresultants(F, G, K)[-1] _, h = dup_primitive(h, K) if K.is_negative(dup_LC(h, K)): c = -c h = dup_mul_ground(h, c, K) cff = dup_quo(f, h, K) cfg = dup_quo(g, h, K) return h, cff, cfg
def dup_discriminant(f, K): """ Computes discriminant of a polynomial in `K[x]`. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_discriminant(x**2 + 2*x + 3) -8 """ d = dup_degree(f) if d <= 0: return K.zero else: s = (-1)**((d * (d - 1)) // 2) c = dup_LC(f, K) r = dup_resultant(f, dup_diff(f, 1, K), K) return K.quo(r, c * K(s))
def dmp_zz_wang_test_points(f, T, ct, A, u, K): """Wang/EEZ: Test evaluation points for suitability. """ if not dmp_eval_tail(dmp_LC(f, K), A, u - 1, K): raise EvaluationFailed('no luck') g = dmp_eval_tail(f, A, u, K) if not dup_sqf_p(g, K): raise EvaluationFailed('no luck') c, h = dup_primitive(g, K) if K.is_negative(dup_LC(h, K)): c, h = -c, dup_neg(h, K) v = u - 1 E = [dmp_eval_tail(t, A, v, K) for t, _ in T] D = dmp_zz_wang_non_divisors(E, c, ct, K) if D is not None: return c, h, E else: raise EvaluationFailed('no luck')
def dup_zz_zassenhaus(f, K): """Factor primitive square-free polynomials in `Z[x]`. """ n = dup_degree(f) if n == 1: return [f] fc = f[-1] A = dup_max_norm(f, K) b = dup_LC(f, K) B = int(abs(K.sqrt(K(n + 1)) * 2**n * A * b)) C = int((n + 1)**(2 * n) * A**(2 * n - 1)) gamma = int(_ceil(2 * _log(C, 2))) bound = int(2 * gamma * _log(gamma)) a = [] # choose a prime number `p` such that `f` be square free in Z_p # if there are many factors in Z_p, choose among a few different `p` # the one with fewer factors for px in range(3, bound + 1): if not isprime(px) or b % px == 0: continue px = K.convert(px) F = gf_from_int_poly(f, px) if not gf_sqf_p(F, px, K): continue fsqfx = gf_factor_sqf(F, px, K)[1] a.append((px, fsqfx)) if len(fsqfx) < 15 or len(a) > 4: break p, fsqf = min(a, key=lambda x: len(x[1])) l = int(_ceil(_log(2 * B + 1, p))) modular = [gf_to_int_poly(ff, p) for ff in fsqf] g = dup_zz_hensel_lift(p, f, modular, l, K) sorted_T = range(len(g)) T = set(sorted_T) factors, s = [], 1 pl = p**l while 2 * s <= len(T): for S in subsets(sorted_T, s): # lift the constant coefficient of the product `G` of the factors # in the subset `S`; if it is does not divide `fc`, `G` does # not divide the input polynomial if b == 1: q = 1 for i in S: q = q * g[i][-1] q = q % pl if not _test_pl(fc, q, pl): continue else: G = [b] for i in S: G = dup_mul(G, g[i], K) G = dup_trunc(G, pl, K) G = dup_primitive(G, K)[1] q = G[-1] if q and fc % q != 0: continue H = [b] S = set(S) T_S = T - S if b == 1: G = [b] for i in S: G = dup_mul(G, g[i], K) G = dup_trunc(G, pl, K) for i in T_S: H = dup_mul(H, g[i], K) H = dup_trunc(H, pl, K) G_norm = dup_l1_norm(G, K) H_norm = dup_l1_norm(H, K) if G_norm * H_norm <= B: T = T_S sorted_T = [i for i in sorted_T if i not in S] G = dup_primitive(G, K)[1] f = dup_primitive(H, K)[1] factors.append(G) b = dup_LC(f, K) break else: s += 1 return factors + [f]
def dup_cyclotomic_p(f, K, irreducible=False): """ Efficiently test if ``f`` is a cyclotomic polnomial. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 >>> R.dup_cyclotomic_p(f) False >>> g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 >>> R.dup_cyclotomic_p(g) True """ if K.is_QQ: try: K0, K = K, K.get_ring() f = dup_convert(f, K0, K) except CoercionFailed: return False elif not K.is_ZZ: return False lc = dup_LC(f, K) tc = dup_TC(f, K) if lc != 1 or (tc != -1 and tc != 1): return False if not irreducible: coeff, factors = dup_factor_list(f, K) if coeff != K.one or factors != [(f, 1)]: return False n = dup_degree(f) g, h = [], [] for i in range(n, -1, -2): g.insert(0, f[i]) for i in range(n - 1, -1, -2): h.insert(0, f[i]) g = dup_sqr(dup_strip(g), K) h = dup_sqr(dup_strip(h), K) F = dup_sub(g, dup_lshift(h, 1, K), K) if K.is_negative(dup_LC(F, K)): F = dup_neg(F, K) if F == f: return True g = dup_mirror(f, K) if K.is_negative(dup_LC(g, K)): g = dup_neg(g, K) if F == g and dup_cyclotomic_p(g, K): return True G = dup_sqf_part(F, K) if dup_sqr(G, K) == F and dup_cyclotomic_p(G, K): return True return False
def dup_inner_subresultants(f, g, K): """ Subresultant PRS algorithm in `K[x]`. Computes the subresultant polynomial remainder sequence (PRS) and the non-zero scalar subresultants of `f` and `g`. By [1] Thm. 3, these are the constants '-c' (- to optimize computation of sign). The first subdeterminant is set to 1 by convention to match the polynomial and the scalar subdeterminants. If 'deg(f) < deg(g)', the subresultants of '(g,f)' are computed. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_inner_subresultants(x**2 + 1, x**2 - 1) ([x**2 + 1, x**2 - 1, -2], [1, 1, 4]) References ========== .. [1] W.S. Brown, The Subresultant PRS Algorithm. ACM Transaction of Mathematical Software 4 (1978) 237-249 """ n = dup_degree(f) m = dup_degree(g) if n < m: f, g = g, f n, m = m, n if not f: return [], [] if not g: return [f], [K.one] R = [f, g] d = n - m b = (-K.one)**(d + 1) h = dup_prem(f, g, K) h = dup_mul_ground(h, b, K) lc = dup_LC(g, K) c = lc**d # Conventional first scalar subdeterminant is 1 S = [K.one, c] c = -c while h: k = dup_degree(h) R.append(h) f, g, m, d = g, h, k, m - k b = -lc * c**d h = dup_prem(f, g, K) h = dup_quo_ground(h, b, K) lc = dup_LC(g, K) if d > 1: # abnormal case q = c**(d - 1) c = K.quo((-lc)**d, q) else: c = -lc S.append(-c) return R, S
def dup_zz_factor(f, K): """ Factor (non square-free) polynomials in `Z[x]`. Given a univariate polynomial `f` in `Z[x]` computes its complete factorization `f_1, ..., f_n` into irreducibles over integers:: f = content(f) f_1**k_1 ... f_n**k_n The factorization is computed by reducing the input polynomial into a primitive square-free polynomial and factoring it using Zassenhaus algorithm. Trial division is used to recover the multiplicities of factors. The result is returned as a tuple consisting of:: (content(f), [(f_1, k_1), ..., (f_n, k_n)) Consider polynomial `f = 2*x**4 - 2`:: >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_zz_factor(2*x**4 - 2) (2, [(x - 1, 1), (x + 1, 1), (x**2 + 1, 1)]) In result we got the following factorization:: f = 2 (x - 1) (x + 1) (x**2 + 1) Note that this is a complete factorization over integers, however over Gaussian integers we can factor the last term. By default, polynomials `x**n - 1` and `x**n + 1` are factored using cyclotomic decomposition to speedup computations. To disable this behaviour set cyclotomic=False. References ========== .. [1] [Gathen99]_ """ cont, g = dup_primitive(f, K) n = dup_degree(g) if dup_LC(g, K) < 0: cont, g = -cont, dup_neg(g, K) if n <= 0: return cont, [] elif n == 1: return cont, [(g, 1)] if query('USE_IRREDUCIBLE_IN_FACTOR'): if dup_zz_irreducible_p(g, K): return cont, [(g, 1)] g = dup_sqf_part(g, K) H = None if query('USE_CYCLOTOMIC_FACTOR'): H = dup_zz_cyclotomic_factor(g, K) if H is None: H = dup_zz_zassenhaus(g, K) factors = dup_trial_division(f, H, K) return cont, factors
def test_dup_LC(): assert dup_LC([], ZZ) == 0 assert dup_LC([2, 3, 4, 5], ZZ) == 2
def dup_zz_heu_gcd(f, g, K): """ Heuristic polynomial GCD in `Z[x]`. Given univariate polynomials `f` and `g` in `Z[x]`, returns their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` such that:: h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) The algorithm is purely heuristic which means it may fail to compute the GCD. This will be signaled by raising an exception. In this case you will need to switch to another GCD method. The algorithm computes the polynomial GCD by evaluating polynomials f and g at certain points and computing (fast) integer GCD of those evaluations. The polynomial GCD is recovered from the integer image by interpolation. The final step is to verify if the result is the correct GCD. This gives cofactors as a side effect. Examples ======== >>> from diofant.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_zz_heu_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 2) References ========== .. [1] [Liao95]_ """ result = _dup_rr_trivial_gcd(f, g, K) if result is not None: return result df = dup_degree(f) dg = dup_degree(g) gcd, f, g = dup_extract(f, g, K) if df == 0 or dg == 0: return [gcd], f, g f_norm = dup_max_norm(f, K) g_norm = dup_max_norm(g, K) B = K(2 * min(f_norm, g_norm) + 29) x = max( min(B, 99 * K.sqrt(B)), 2 * min(f_norm // abs(dup_LC(f, K)), g_norm // abs(dup_LC(g, K))) + 2) for i in range(0, HEU_GCD_MAX): ff = dup_eval(f, x, K) gg = dup_eval(g, x, K) if ff and gg: h = K.gcd(ff, gg) cff = ff // h cfg = gg // h h = _dup_zz_gcd_interpolate(h, x, K) h = dup_primitive(h, K)[1] cff_, r = dup_div(f, h, K) if not r: cfg_, r = dup_div(g, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff_, cfg_ cff = _dup_zz_gcd_interpolate(cff, x, K) h, r = dup_div(f, cff, K) if not r: cfg_, r = dup_div(g, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff, cfg_ cfg = _dup_zz_gcd_interpolate(cfg, x, K) h, r = dup_div(g, cfg, K) if not r: cff_, r = dup_div(f, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff_, cfg x = 73794 * x * K.sqrt(K.sqrt(x)) // 27011 raise HeuristicGCDFailed('no luck')