Nice work! Couple of questions: what does zzx stand for?
Wouldn't this be the case (lot's of functions with the same prefix) where you should use a class and the methods should be static methods (instead of using prefixes all the time)? Mateusz Paprocki wrote: > This patch reimplements univariate polynomials over integers and > factorization algorithm over this domain. The module was written > to match galoispolys.py design. All functionality available in > polynomials/fast/intpoly.py was moved to the new module. > > All subroutines (especially zzx_mod_gcd) use functions provided > by galoispolys.py module. The most important new development is > zzx_heu_gcd - heuristic polynomial GCD. > > The new factorization code is 4 times faster on average than the > old code. Many improvements are possible, especially recombination > phase in Zassenhaus algorithm should be reconsidered. > --- > sympy/polys/integerpolys.py | 869 > ++++++++++++++++++++++++++++++++ > sympy/polys/tests/test_integerpolys.py | 307 +++++++++++ > 2 files changed, 1176 insertions(+), 0 deletions(-) > create mode 100644 sympy/polys/integerpolys.py > create mode 100644 sympy/polys/tests/test_integerpolys.py > > diff --git a/sympy/polys/integerpolys.py b/sympy/polys/integerpolys.py > new file mode 100644 > index 0000000..4c81428 > --- /dev/null > +++ b/sympy/polys/integerpolys.py > @@ -0,0 +1,869 @@ > +"""Univariate polynomials with coefficients in the ring of integers. """ > + > +from sympy.polys.galoispolys import gf_from_int_poly, gf_to_int_poly, > gf_degree, \ > + gf_from_dict, gf_mul, gf_quo, gf_gcd, gf_gcdex, gf_sqf_p, gf_factor_sqf > + > +from sympy.ntheory import randprime, isprime > + > +from sympy.utilities.iterables import subsets > +from sympy.ntheory.modular import crt1, crt2 > +from sympy.core.numbers import igcd, igcdex > + > +from math import floor, ceil, sqrt, log > + > +class HeuristicGCDFailed(Exception): > + pass > + > +def zzx_degree(f): > + """Returns leading degree of f. """ > + return len(f) - 1 > + > +def zzx_LC(f): > + """Returns leading coefficient of f. """ > + if not f: > + return 0 > + else: > + return f[0] > + > +def zzx_TC(f): > + """Returns trailing coefficient of f. """ > + if not f: > + return 0 > + else: > + return f[-1] > + > +def zzx_nth(f, n): > + """Returns n-th coefficient of f. """ > + return f[zzx_degree(f)-n] > + > +def zzx_strip(f): > + """Remove leading zeros from f. """ > + if not f or f[0]: > + return f > + > + k = 0 > + > + for coeff in f: > + if coeff: > + break > + else: > + k += 1 > + > + return f[k:] > + > +def zzx_from_dict(f): > + """Create Z[x] polynomial from a dict. """ > + n, h = max(f.iterkeys()), [] > + > + for k in xrange(n, -1, -1): > + h.append(f.get(k, 0)) > + > + return zzx_strip(h) > + > +def zzx_to_dict(f): > + """Convert Z[x] polynomial to a dict. """ > + n, result = zzx_degree(f), {} > + > + for i in xrange(0, n+1): > + if f[n-i]: > + result[i] = f[n-i] > + > + return result > + > +def zzx_add_term(f, c, k): > + """Add c*x**k to f over Z[x]. """ > + if not c: > + return f > + > + n = len(f) > + m = n-k-1 > + > + if k == n-1: > + return zzx_strip([f[0]+c] + f[1:]) > + else: > + if k >= n: > + return [c] + [0] * (k-n) + f > + else: > + return f[:m] + [f[m]+c] + f[m+1:] > + > +def zzx_sub_term(f, c, k): > + """Subtract c*x**k from f over Z[x]. """ > + if not c: > + return f > + > + n = len(f) > + m = n-k-1 > + > + if k == n-1: > + return zzx_strip([f[0]-c] + f[1:]) > + else: > + if k >= n: > + return [-c] + [0] * (k-n) + f > + else: > + return f[:m] + [f[m]-c] + f[m+1:] > + > +def zzx_mul_term(f, c, k): > + """Multiply f with c*x**k over Z[x]. """ > + if not c or not f: > + return [] > + else: > + return [ c * cc for cc in f ] + [0] * k > + > +def zzx_abs(f): > + """Make all coefficients positive. """ > + return [ abs(coeff) for coeff in f ] > + > +def zzx_neg(f): > + """Negate a polynomial over Z[x]. """ > + return [ -coeff for coeff in f ] > + > +def zzx_add(f, g): > + """Add polynomials over Z[x]. """ > + if not f: > + return g > + if not g: > + return f > + > + df = zzx_degree(f) > + dg = zzx_degree(g) > + > + if df == dg: > + return zzx_strip([ a + b for a, b in zip(f, g) ]) > + else: > + k = abs(df - dg) > + > + if df > dg: > + h, f = f[:k], f[k:] > + else: > + h, g = g[:k], g[k:] > + > + return h + [ a + b for a, b in zip(f, g) ] > + > +def zzx_sub(f, g): > + """Subtract polynomials over Z[x]. """ > + if not g: > + return f > + if not f: > + return zzx_neg(g) > + > + df = zzx_degree(f) > + dg = zzx_degree(g) > + > + if df == dg: > + return zzx_strip([ a - b for a, b in zip(f, g) ]) > + else: > + k = abs(df - dg) > + > + if df > dg: > + h, f = f[:k], f[k:] > + else: > + h, g = zzx_neg(g[:k]), g[k:] > + > + return h + [ a - b for a, b in zip(f, g) ] > + > +def zzx_add_mul(f, g, h): > + """Returns f + g*h where f, g, h in Z[x]. """ > + return zzx_add(f, zzx_mul(g, h)) > + > +def zzx_sub_mul(f, g, h): > + """Returns f - g*h where f, g, h in Z[x]. """ > + return zzx_sub(f, zzx_mul(g, h)) > + > +def zzx_mul(f, g): > + """Multiply polynomials over Z[x]. """ > + df = zzx_degree(f) > + dg = zzx_degree(g) > + > + dh = df + dg > + h = [0]*(dh+1) > + > + for i in xrange(0, dh+1): > + coeff = 0 > + > + for j in xrange(max(0, i-dg), min(i, df)+1): > + coeff += f[j]*g[i-j] > + > + h[i] = coeff > + > + return zzx_strip(h) > + > +def zzx_sqr(f): > + """Square polynomials over Z[x]. """ > + df = zzx_degree(f) > + > + dh = 2*df > + h = [0]*(dh+1) > + > + for i in xrange(0, dh+1): > + coeff = 0 > + > + jmin = max(0, i-df) > + jmax = min(i, df) > + > + n = jmax - jmin + 1 > + > + jmax = jmin + n // 2 - 1 > + > + for j in xrange(jmin, jmax+1): > + coeff += f[j]*f[i-j] > + > + coeff += coeff > + > + if n & 1: > + elem = f[jmax+1] > + coeff += elem**2 > + > + h[i] = coeff > + > + return zzx_strip(h) > + > +def zzx_div(f, g): > + """Returns quotient and remainder over Z[x]. """ > + df = zzx_degree(f) > + dg = zzx_degree(g) > + > + if not g: > + raise ZeroDivisionError("polynomial division") > + elif df < dg: > + return [], f > + > + q, r = [], f > + > + while True: > + deg_r = zzx_degree(r) > + deg_g = zzx_degree(g) > + > + if deg_r < deg_g: > + break > + > + lc_r = zzx_LC(r) > + lc_g = zzx_LC(g) > + > + if lc_r % lc_g != 0: > + break > + > + c = lc_r // lc_g > + k = deg_r - deg_g > + > + q = zzx_add_term(q, c, k) > + h = zzx_mul_term(g, c, k) > + r = zzx_sub(r, h) > + > + return q, r > + > +def zzx_quo(f, g): > + """Returns polynomial remainder over Z[x]. """ > + return zzx_div(f, g)[0] > + > +def zzx_rem(f, g): > + """Returns polynomial remainder over Z[x]. """ > + return zzx_div(f, g)[1] > + > +def zzx_max_norm(f): > + """Returns maximum norm of a polynomial. """ > + if not f: > + return 0 > + else: > + return max(zzx_abs(f)) > + > +def zzx_l1_norm(f): > + """Returns l1 norm of a polynomial. """ > + if not f: > + return 0 > + else: > + return sum(zzx_abs(f)) > + > +def zzx_diff(f): > + """Differentiate polynomial over Z[x]. """ > + n, deriv = zzx_degree(f), [] > + > + for coeff in f[:-1]: > + deriv.append(n*coeff) > + n -= 1 > + > + return deriv > + > +def zzx_eval(f, x): > + """Evaluate f(x) using Horner scheme. """ > + result = 0 > + > + for a in f: > + result *= x > + result += a > + > + return result > + > +def zzx_trunc(f, m): > + """Reduce Z[x] polynomial modulo m. """ > + g = [] > + > + for coeff in f: > + coeff %= m > + > + if coeff > m // 2: > + g.append(coeff - m) > + else: > + g.append(coeff) > + > + return zzx_strip(g) > + > +def zzx_content(f): > + """Returns integer GCD of coefficients. """ > + cont = 0 > + > + for coeff in f: > + cont = igcd(cont, coeff) > + > + if cont == 1: > + break > + > + return cont > + > +def zzx_primitive(f): > + """Divides all coefficients by content. """ > + cont = zzx_content(f) > + > + if cont == 1: > + return 1, f > + else: > + return cont, [ coeff // cont for coeff in f ] > + > +def zzx_sqf_part(f): > + """Returns square-free part of a polynomial over Z[x]. """ > + quo = zzx_quo(f, zzx_gcd(f, zzx_diff(f))) > + return zzx_primitive(quo)[1] > + > +def zzx_gcd(f, g, **flags): > + """Polynomial GCD over Z[x]. """ > + if flags.get("heu", True): > + try: > + return zzx_heu_gcd(f, g)[0] > + except HeuristicGCDFailed: > + pass > + > + return zzx_mod_gcd(f, g)[0] > + > +def zzx_heu_gcd(f, g, **flags): > + """Heuristic polynomial GCD over Z[x]. > + > + Given univariate polynomials f and g over 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. In this case an exception is raised. However it seems that > + this algorithm fails very rarely. In case of failure one can switch > + to modular GCD algorithm (zzx_mod_gcd). In high-level code, instead > + of direct application of heuristic GCD method, use zzx_gcd function > + which will do the switch automatically when necessary. > + > + 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 correctness of the > + result. By default the algorithm will perform at most six tries > + to compute the GCD. > + > + Note this method can be easily adapted to multivariate case. The > + only things to change is to modify interpolation function and add > + recursive invocation of the heuristic gcd algorithm. > + > + For more details on the implemented algorithm refer to: > + > + [1] Hsin-Chao Liao, R. Fateman, Evaluation of the heuristic polynomial > + GCD, International Symposium on Symbolic and Algebraic Computation > + (ISSAC), ACM Press, Montreal, Quebec, Canada, 1995, pp. 240--247 > + > + """ > + def sp_interpolate(h, c): > + f = [] > + > + while h: > + rem = h % c > + > + if rem > c // 2: > + rem -= c > + > + f.insert(0, rem) > + h = (h-rem) // c > + > + return zzx_strip(f) > + > + df = zzx_degree(f) > + dg = zzx_degree(g) > + > + cf = zzx_content(f) > + cg = zzx_content(g) > + > + h = igcd(cf, cg) > + > + f = [ c // h for c in f ] > + g = [ c // h for c in g ] > + > + if df <= 0 or dg <= 0: > + return zzx_strip([h]), f, g > + else: > + gcd = h > + > + f_norm = zzx_max_norm(f) > + g_norm = zzx_max_norm(g) > + > + B = 2*min(f_norm, g_norm) + 29 > + > + x = max(min(B, 99*sqrt(B)), > + 2*min(f_norm // abs(zzx_LC(f)), > + g_norm // abs(zzx_LC(g))) + 2) > + > + for i in xrange(0, 6): > + length_x = int(floor(log(x, 2)) + 1) > + > + if length_x * max(df, dg) > 4000: > + raise HeuristicGCDFailed > + > + ff = zzx_eval(f, x) > + gg = zzx_eval(g, x) > + > + h = igcd(ff, gg) > + > + cff = ff // h > + cfg = gg // h > + > + h = sp_interpolate(h, x) > + h = zzx_primitive(h)[1] > + > + cff_, r = zzx_div(f, h) > + > + if not r: > + cfg_, r = zzx_div(g, h) > + > + if not r: > + h = zzx_mul_term(h, gcd, 0) > + return h, cff_, cfg_ > + > + cff = sp_interpolate(cff, x) > + > + h, r = zzx_div(f, cff) > + > + if not r: > + cfg_, r = zzx_div(g, h) > + > + if not r: > + h = zzx_mul_term(h, gcd, 0) > + return h, cff, cfg_ > + > + cfg = sp_interpolate(cfg, x) > + > + h, r = zzx_div(g, cfg) > + > + if not r: > + cff_, r = zzx_div(f, h) > + > + if not r: > + h = zzx_mul_term(h, gcd, 0) > + return h, cff_, cfg > + > + x = int(2.7319*x*sqrt(sqrt(x))) > + > + raise HeuristicGCDFailed > + > +def zzx_mod_gcd(f, g, **flags): > + """Modular small primes polynomial GCD over Z[x]. > + > + Given univariate polynomials f and g over 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 uses modular small primes approach. It works by > + computing several GF(p)[x] GCDs for a set of randomly chosen > + primes and uses Chinese Remainder Theorem to recover the GCD > + over Z[x] from its images. > + > + The algorithm is probabilistic which means it never fails, > + however its running time depends on the number of unlucky > + primes chosen for computing GF(p)[x] images. > + > + 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. 158 > + > + """ > + n = zzx_degree(f) > + m = zzx_degree(g) > + > + cf = zzx_content(f) > + cg = zzx_content(g) > + > + h = igcd(cf, cg) > + > + f = [ c // h for c in f ] > + g = [ c // h for c in g ] > + > + if n <= 0 or m <= 0: > + return zzx_strip([h]), f, g > + else: > + gcd = h > + > + A = max(zzx_abs(f) + zzx_abs(g)) > + b = igcd(zzx_LC(f), zzx_LC(g)) > + > + B = int(ceil(2**n*A*b*sqrt(n + 1))) > + k = int(ceil(2*b*log((n + 1)**n*A**(2*n), 2))) > + l = int(ceil(log(2*B + 1, 2))) > + > + prime_max = max(int(ceil(2*k*log(k))), 51) > + > + while True: > + while True: > + primes = set([]) > + unlucky = set([]) > + > + ff, gg, hh = {}, {}, {} > + > + while len(primes) < l: > + p = randprime(3, prime_max+1) > + > + if (p in primes) and (b % p == 0): > + continue > + > + F = gf_from_int_poly(f, p) > + G = gf_from_int_poly(g, p) > + > + H = gf_gcd(F, G, p) > + > + primes.add(p) > + > + ff[p] = F > + gg[p] = G > + hh[p] = H > + > + e = min([ gf_degree(h) for h in hh.itervalues() ]) > + > + for p in set(primes): > + if gf_degree(hh[p]) != e: > + primes.remove(p) > + unlucky.add(p) > + > + del ff[p] > + del gg[p] > + del hh[p] > + > + if len(primes) < l // 2: > + continue > + > + while len(primes) < l: > + p = randprime(3, prime_max+1) > + > + if (p in primes) or (p in unlucky) or (b % p == 0): > + continue > + > + F = gf_from_int_poly(f, p) > + G = gf_from_int_poly(g, p) > + > + H = gf_gcd(F, G, p) > + > + if gf_degree(H) != e: > + unlucky.add(p) > + else: > + primes.add(p) > + > + ff[p] = F > + gg[p] = G > + hh[p] = H > + > + break > + > + fff, ggg = {}, {} > + > + for p in primes: > + fff[p] = gf_quo(ff[p], hh[p], p) > + ggg[p] = gf_quo(gg[p], hh[p], p) > + > + F, G, H = [], [], [] > + > + crt_mm, crt_e, crt_s = crt1(primes) > + > + for i in xrange(0, e + 1): > + C = [ b * zzx_nth(hh[p], i) for p in primes ] > + c = crt2(primes, C, crt_mm, crt_e, crt_s, True) > + > + H.insert(0, c) > + > + H = zzx_strip(H) > + > + for i in xrange(0, zzx_degree(f) - e + 1): > + C = [ zzx_nth(fff[p], i) for p in primes ] > + c = crt2(primes, C, crt_mm, crt_e, crt_s, True) > + > + F.insert(0, c) > + > + for i in xrange(0, zzx_degree(g) - e + 1): > + C = [ zzx_nth(ggg[p], i) for p in primes ] > + c = crt2(primes, C, crt_mm, crt_e, crt_s, True) > + > + G.insert(0, c) > + > + H_norm = zzx_l1_norm(H) > + > + F_norm = zzx_l1_norm(F) > + G_norm = zzx_l1_norm(G) > + > + if H_norm*F_norm <= B and H_norm*G_norm <= B: > + break > + > + return zzx_mul_term(H, gcd, 0), F, G > + > +def zzx_hensel_step(m, f, g, h, s, t): > + """One step in Hensel lifting. > + > + Given positive integer m and Z[x] polynomials f, g, h, s and t such > that: > + > + [1] f == g*h (mod m) > + [2] s*g + t*h == 1 (mod m) > + > + [3] lc(f) not a zero divisor (mod m) > + [4] lc(h) == 1 > + > + [5] deg(f) == deg(g) + deg(h) > + [6] deg(s) < deg(h) > + [7] deg(t) < deg(g) > + > + returns polynomials G, H, S and T, such that: > + > + [A] f == G*H (mod m**2) > + [B] S*G + T**H == 1 (mod m**2) > + > + 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. 418 > + > + """ > + M = m**2 > + > + e = zzx_sub_mul(f, g, h) > + e = zzx_trunc(e, M) > + > + q, r = zzx_div(zzx_mul(s, e), h) > + > + q = zzx_trunc(q, M) > + r = zzx_trunc(r, M) > + > + u = zzx_add(zzx_mul(t, e), zzx_mul(q, g)) > + G = zzx_trunc(zzx_add(g, u), M) > + H = zzx_trunc(zzx_add(h, r), M) > + > + u = zzx_add(zzx_mul(s, G), zzx_mul(t, H)) > + b = zzx_trunc(zzx_sub(u, [1]), M) > + > + c, d = zzx_div(zzx_mul(s, b), H) > + > + c = zzx_trunc(c, M) > + d = zzx_trunc(d, M) > + > + u = zzx_add(zzx_mul(t, b), zzx_mul(c, G)) > + S = zzx_trunc(zzx_sub(s, d), M) > + T = zzx_trunc(zzx_sub(t, u), M) > + > + return G, H, S, T > + > +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_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_factor(f): > + """Factor (non square-free) polynomials over Z[x]. > + > + Given a univariate polynomial f over 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 = x**4 - 1: > + > + >>> zzx_factor([2, 0, 0, 0, -2]) > + (2, [([1, -1], 1), ([1, 1], 1), ([1, 0, 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. > + > + 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. 427 > + """ > + cont, g = zzx_primitive(f) > + > + if zzx_degree(g) < 1: > + return cont, [] > + > + if zzx_LC(g) < 0: > + g = zzx_neg(g) > + cont = -cont > + > + g = zzx_sqf_part(g) > + > + factors = [] > + > + for h in zzx_zassenhaus(g): > + k = 0 > + > + while True: > + q, r = zzx_div(f, h) > + > + if not r: > + f, k = q, k+1 > + else: > + break > + > + factors.append((h, k)) > + > + def compare((f_a, e_a), (f_b, e_b)): > + i = len(f_a) - len(f_b) > + > + if not i: > + j = e_a - e_b > + > + if not j: > + return cmp(f_a, f_b) > + else: > + return j > + else: > + return i > + > + return cont, sorted(factors, compare) > + > diff --git a/sympy/polys/tests/test_integerpolys.py > b/sympy/polys/tests/test_integerpolys.py > new file mode 100644 > index 0000000..6e13ded > --- /dev/null > +++ b/sympy/polys/tests/test_integerpolys.py > @@ -0,0 +1,307 @@ > + > +from sympy.polys.integerpolys import ( > + zzx_degree, zzx_strip, > + zzx_from_dict, zzx_to_dict, > + zzx_LC, zzx_TC, > + zzx_abs, zzx_neg, > + zzx_add_term, zzx_sub_term, zzx_mul_term, > + zzx_add, zzx_sub, zzx_mul, zzx_sqr, > + zzx_div, zzx_quo, zzx_rem, > + zzx_heu_gcd, zzx_mod_gcd, > + zzx_max_norm, zzx_l1_norm, > + zzx_diff, zzx_eval, zzx_trunc, > + zzx_sqf_part, > + zzx_content, zzx_primitive, > + zzx_hensel_step, zzx_hensel_lift, > + zzx_zassenhaus, zzx_factor) > + > +from sympy import raises > + > +def test_zzx_degree(): > + assert zzx_degree([]) == -1 > + assert zzx_degree([1]) == 0 > + assert zzx_degree([1,0]) == 1 > + assert zzx_degree([1,0,0,0,1]) == 4 > + > +def test_zzx_strip(): > + assert zzx_strip([]) == [] > + assert zzx_strip([0]) == [] > + assert zzx_strip([0,0,0]) == [] > + > + assert zzx_strip([1]) == [1] > + assert zzx_strip([0,1]) == [1] > + assert zzx_strip([0,0,0,1]) == [1] > + > + assert zzx_strip([1,2,0]) == [1,2,0] > + assert zzx_strip([0,1,2,0]) == [1,2,0] > + assert zzx_strip([0,0,0,1,2,0]) == [1,2,0] > + > +def test_zzx_from_to_dict(): > + f = {8: 3, 5: 2, 0: 8} > + g = [3,0,0,2,0,0,0,0,8] > + > + assert zzx_from_dict(f) == g > + assert zzx_to_dict(g) == f > + > +def test_zzx_add_term(): > + assert zzx_add_term([], 0, 0) == [] > + > + assert zzx_add_term([], 1, 0) == [1] > + assert zzx_add_term([], 1, 1) == [1, 0] > + assert zzx_add_term([], 1, 2) == [1, 0, 0] > + > + assert zzx_add_term([1,1,1], 1, 0) == [1, 1, 2] > + assert zzx_add_term([1,1,1], 1, 1) == [1, 2, 1] > + assert zzx_add_term([1,1,1], 1, 2) == [2, 1, 1] > + > + assert zzx_add_term([1,1,1], 1, 3) == [1, 1, 1, 1] > + assert zzx_add_term([1,1,1], 1, 4) == [1, 0, 1, 1, 1] > + assert zzx_add_term([1,1,1], 1, 5) == [1, 0, 0, 1, 1, 1] > + assert zzx_add_term([1,1,1], 1, 6) == [1, 0, 0, 0, 1, 1, 1] > + > + assert zzx_add_term([1,1,1], -1, 2) == [1, 1] > + > +def test_zzx_sub_term(): > + assert zzx_sub_term([], 0, 0) == [] > + > + assert zzx_sub_term([], 1, 0) == [-1] > + assert zzx_sub_term([], 1, 1) == [-1, 0] > + assert zzx_sub_term([], 1, 2) == [-1, 0, 0] > + > + assert zzx_sub_term([1,1,1], 2, 0) == [ 1, 1,-1] > + assert zzx_sub_term([1,1,1], 2, 1) == [ 1,-1, 1] > + assert zzx_sub_term([1,1,1], 2, 2) == [-1, 1, 1] > + > + assert zzx_sub_term([1,1,1], 1, 3) == [-1, 1, 1, 1] > + assert zzx_sub_term([1,1,1], 1, 4) == [-1, 0, 1, 1, 1] > + assert zzx_sub_term([1,1,1], 1, 5) == [-1, 0, 0, 1, 1, 1] > + assert zzx_sub_term([1,1,1], 1, 6) == [-1, 0, 0, 0, 1, 1, 1] > + > + assert zzx_sub_term([1,1,1], 1, 2) == [1, 1] > + > +def test_zzx_mul_term(): > + assert zzx_mul_term([], 2, 3) == [] > + assert zzx_mul_term([1,1], 0, 3) == [] > + > + assert zzx_mul_term([1,2,3], 2, 0) == [2,4,6] > + assert zzx_mul_term([1,2,3], 2, 1) == [2,4,6,0] > + assert zzx_mul_term([1,2,3], 2, 2) == [2,4,6,0,0] > + assert zzx_mul_term([1,2,3], 2, 3) == [2,4,6,0,0,0] > + > +def test_zzx_abs(): > + assert zzx_abs([-1,2,3]) == [1,2,3] > + > +def test_zzx_neg(): > + assert zzx_neg([-1,2,3]) == [1,-2,-3] > + > +def test_zzx_add(): > + assert zzx_add([], []) == [] > + assert zzx_add([1], []) == [1] > + assert zzx_add([], [1]) == [1] > + assert zzx_add([1], [1]) == [2] > + assert zzx_add([1], [2]) == [3] > + > + assert zzx_add([1,2], [1]) == [1,3] > + assert zzx_add([1], [1,2]) == [1,3] > + > + assert zzx_add([1,2,3], [8,9,10]) == [9,11,13] > + > +def test_zzx_sub(): > + assert zzx_sub([], []) == [] > + assert zzx_sub([1], []) == [1] > + assert zzx_sub([], [1]) == [-1] > + assert zzx_sub([1], [1]) == [] > + assert zzx_sub([1], [2]) == [-1] > + > + assert zzx_sub([1,2], [1]) == [1,1] > + assert zzx_sub([1], [1,2]) == [-1,-1] > + > + assert zzx_sub([3,2,1], [8,9,10]) == [-5,-7,-9] > + > +def test_zzx_mul(): > + assert zzx_mul([], []) == [] > + assert zzx_mul([], [1]) == [] > + assert zzx_mul([1], []) == [] > + assert zzx_mul([1], [1]) == [1] > + assert zzx_mul([5], [7]) == [35] > + > + assert zzx_mul([3,0,0,6,1,2], [4,0,1,0]) == [12,0,3,24,4,14,1,2,0] > + assert zzx_mul([4,0,1,0], [3,0,0,6,1,2]) == [12,0,3,24,4,14,1,2,0] > + > + assert zzx_mul([2,0,0,1,7], [2,0,0,1,7]) == [4,0,0,4,28,0,1,14,49] > + > +def test_zzx_sqr(): > + assert zzx_sqr([]) == [] > + assert zzx_sqr([2]) == [4] > + assert zzx_sqr([1,2]) == [1,4,4] > + > + assert zzx_sqr([2,0,0,1,7]) == [4,0,0,4,28,0,1,14,49] > + > +def test_zzx_div(): > + raises(ZeroDivisionError, "zzx_div([1,2,3], [])") > + raises(ZeroDivisionError, "zzx_quo([1,2,3], [])") > + raises(ZeroDivisionError, "zzx_rem([1,2,3], [])") > + > + f, g, q, r = [5,4,3,2,1], [1,2,3], [5,-6,0], [20,1] > + > + assert zzx_div(f, g) == (q, r) > + assert zzx_quo(f, g) == q > + assert zzx_rem(f, g) == r > + > + f, g, q, r = [5,4,3,2,1,0], [1,2,0,0,9], [5,-6], [15,2,-44,54] > + > + assert zzx_div(f, g) == (q, r) > + assert zzx_quo(f, g) == q > + assert zzx_rem(f, g) == r > + > +def test_zzx_gcd(): > + assert zzx_heu_gcd([], []) == ([], [], []) > + assert zzx_mod_gcd([], []) == ([], [], []) > + > + assert zzx_heu_gcd([2], []) == ([2], [1], []) > + assert zzx_mod_gcd([2], []) == ([2], [1], []) > + > + assert zzx_heu_gcd([2], [2]) == ([2], [1], [1]) > + assert zzx_mod_gcd([2], [2]) == ([2], [1], [1]) > + > + assert zzx_heu_gcd([1,2,1], [1]) == ([1], [1, 2, 1], [1]) > + assert zzx_mod_gcd([1,2,1], [1]) == ([1], [1, 2, 1], [1]) > + > + assert zzx_heu_gcd([1,2,1], [2]) == ([1], [1, 2, 1], [2]) > + assert zzx_mod_gcd([1,2,1], [2]) == ([1], [1, 2, 1], [2]) > + > + assert zzx_heu_gcd([2,4,2], [2]) == ([2], [1, 2, 1], [1]) > + assert zzx_mod_gcd([2,4,2], [2]) == ([2], [1, 2, 1], [1]) > + > + assert zzx_heu_gcd([2], [2,4,2]) == ([2], [1], [1, 2, 1]) > + assert zzx_mod_gcd([2], [2,4,2]) == ([2], [1], [1, 2, 1]) > + > + assert zzx_heu_gcd([2,4,2], [1,1]) == ([1, 1], [2, 2], [1]) > + assert zzx_mod_gcd([2,4,2], [1,1]) == ([1, 1], [2, 2], [1]) > + > + assert zzx_heu_gcd([1,1], [2,4,2]) == ([1, 1], [1], [2, 2]) > + assert zzx_mod_gcd([1,1], [2,4,2]) == ([1, 1], [1], [2, 2]) > + > + f = [1,8,21,22,8] > + g = [1,6,11,6] > + > + h = [1,3,2] > + > + cff = [1,5,4] > + cfg = [1,3] > + > + assert zzx_heu_gcd(f, g) == (h, cff, cfg) > + assert zzx_mod_gcd(f, g) == (h, cff, cfg) > + > + f = [1,0,0,0,-4] > + g = [1,0,4,0, 4] > + > + h = [1,0,2] > + > + cff = [1,0,-2] > + cfg = [1,0, 2] > + > + assert zzx_heu_gcd(f, g) == (h, cff, cfg) > + assert zzx_mod_gcd(f, g) == (h, cff, cfg) > + > +def test_zzx_norm(): > + assert zzx_max_norm([]) == 0 > + assert zzx_max_norm([1]) == 1 > + assert zzx_max_norm([1,4,2,3]) == 4 > + > + assert zzx_l1_norm([]) == 0 > + assert zzx_l1_norm([1]) == 1 > + assert zzx_l1_norm([1,4,2,3]) == 10 > + > +def test_zzx_diff(): > + assert zzx_diff([]) == [] > + assert zzx_diff([7]) == [] > + assert zzx_diff([2,7]) == [2] > + assert zzx_diff([1,2,1]) == [2,2] > + assert zzx_diff([1,2,3,4]) == [3,4,3] > + assert zzx_diff([1,-1,0,0,2]) == [4,-3,0,0] > + > +def test_zzx_eval(): > + assert zzx_eval([], 7) == 0 > + assert zzx_eval([1,2,3], 7) == 66 > + > +def test_zzx_trunc(): > + assert zzx_trunc([1,2,3,4,5,6], 3) == [1, -1, 0, 1, -1, 0] > + assert zzx_trunc([6,5,4,3,2,1], 3) == [-1, 1, 0, -1, 1] > + > +def test_zzx_content(): > + assert zzx_content([]) == 0 > + assert zzx_content([1]) == 1 > + assert zzx_content([1,1]) == 1 > + assert zzx_content([2,2]) == 2 > + assert zzx_content([1,2,1]) == 1 > + assert zzx_content([2,4,2]) == 2 > + > +def test_zzx_primitive(): > + assert zzx_primitive([]) == (0, []) > + assert zzx_primitive([1]) == (1, [1]) > + assert zzx_primitive([1,1]) == (1, [1,1]) > + assert zzx_primitive([2,2]) == (2, [1,1]) > + assert zzx_primitive([1,2,1]) == (1, [1,2,1]) > + assert zzx_primitive([2,4,2]) == (2, [1,2,1]) > + > +def test_zzx_sqf_part(): > + assert zzx_sqf_part([2,3,0,0]) == [2,3,0] > + assert zzx_sqf_part([1,0,1,1]) == [1,0,1,1] > + > +def test_zzx_hensel_step(): > + f = zzx_from_dict({4:1, 0:-1}) > + > + g = zzx_from_dict({3:1, 2:2, 1:-1, 0:-2}) > + h = zzx_from_dict({1:1, 0:-2}) > + s = zzx_from_dict({0:-2}) > + t = zzx_from_dict({2:2, 1:-2, 0:-1}) > + > + G, H, S, T = zzx_hensel_step(5, f, g, h, s, t) > + > + assert G == zzx_from_dict({3:1, 2:7, 1:-1, 0:-7}) > + assert H == zzx_from_dict({1:1, 0:-7}) > + assert S == zzx_from_dict({0:8}) > + assert T == zzx_from_dict({2:-8, 1:-12, 0:-1}) > + > +def test_zzx_hensel_lift(): > + f = zzx_from_dict({4:1, 0:-1}) > + > + f1 = zzx_from_dict({1:1, 0:-1}) > + f2 = zzx_from_dict({1:1, 0:-2}) > + f3 = zzx_from_dict({1:1, 0: 2}) > + f4 = zzx_from_dict({1:1, 0: 1}) > + > + ff_list = zzx_hensel_lift(5, f, [f1, f2, f3, f4], 4) > + > + assert zzx_to_dict(ff_list[0]) == {0: -1, 1: 1} > + assert zzx_to_dict(ff_list[1]) == {0: -182, 1: 1} > + assert zzx_to_dict(ff_list[2]) == {0: 182, 1: 1} > + assert zzx_to_dict(ff_list[3]) == {0: 1, 1: 1} > + > +def test_zzx_factor(): > + f = [1,0,0,1,1] > + > + for i in xrange(0, 20): > + assert zzx_factor(f) == (1, [(f, 1)]) > + > + assert zzx_factor([2,4,2]) == \ > + (2, [([1, 1], 2)]) > + > + assert zzx_factor([1,-6,11,-6]) == \ > + (1, [([1,-3], 1), > + ([1,-2], 1), > + ([1,-1], 1)]) > + > + assert zzx_factor([-1,0,0,0,1,0,0]) == \ > + (-1, [([1,-1], 1), > + ([1, 1], 1), > + ([1, 0], 2), > + ([1, 0, 1], 1)]) > + > + assert zzx_factor(zzx_from_dict({10:1, 0:-1})) == \ > + (1, [([1,-1], 1), > + ([1, 1], 1), > + ([1,-1, 1,-1, 1], 1), > + ([1, 1, 1, 1, 1], 1)]) > + --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "sympy-patches" group. To post to this group, send email to sympy-patches@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sympy-patches?hl=en -~----------~----~----~----~------~----~------~--~---