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
        }
    }
Beispiel #2
0
def zassenhaus(f):
    """Factors a square-free primitive polynomial.

    Returns a list of the unique factors.
    """

    def subsets(M, k):
        """Generates all k-subsets of M."""
        def recursion(result, M, k):
            if k == 0:
                 yield result
            else:
                for i, result2 in enumerate(M[0 : len(M) + 1 - k]):
                    for el in recursion(result + [result2], M[i + 1:], k - 1):
                        yield el

        for i, result in enumerate(M[0 : len(M) + 1 - k]):
            for el in recursion([result], M[i + 1:], k - 1):
                yield el

    n = f.degree
    if n == 1:
        return [f]
    A = max([abs(c) for c in f.coeffs.itervalues()])
    b = f[n]
    B = int(math.sqrt(n+1)*2**n*A*b)
    C = (n+1)**(2*n)*A**(2*n-1)
    gamma = int(math.ceil(2*math.log(C, 2)))
    prime_border = int(2*gamma*math.log(gamma)) + 1

    # Choose a prime.
    while True:
        p = ntheory.generate.randprime(2, prime_border)
        if b % p:
            poly_type = gfpoly.GFPolyFactory(p)
            ff = poly_type.from_int_dict(f.coeffs)
            gg = gfpoly.gcd(ff, ff.diff())
            if gg.degree == 0:
                break
    l = int(math.ceil(math.log(2*B + 1, p)))

    # Modular factorization.
    mod_factors = gfpoly.factor_sqf(ff)
    bb = mod_factors[0] # Leading coefficient mod p.
    h = [IntPoly(hh.to_sym_int_dict()) for hh in mod_factors[1:]]

    # Hensel lifting.
    g = multi_hensel_lift(p, f, h, l)

    # Factor combination and trial division.
    G = []
    T = range(len(g))
    s = 1
    while 2*s <= len(T):
        for S in subsets(T, s):
            gg = IntPoly({0:b})
            for i in S:
                gg *= g[i]
            gg = gg.mod_int(p**l, symmetric=True)
            hh = IntPoly({0:b})
            for i in [i for i in T if i not in S]: # T \ S
                hh *= g[i]
            hh = hh.mod_int(p**l, symmetric=True)

            gg_norm = sum([abs(c) for c in gg.coeffs.itervalues()])
            hh_norm = sum([abs(c) for c in hh.coeffs.itervalues()])
            if gg_norm*hh_norm <= B: # Found divisor
                T = [i for i in T if i not in S] # T \ S
                G.append(gg.primitive()[1])
                f = hh.primitive()[1]
                b = f[f.degree]
                break
        else: # No factors of degree s
            s += 1
    G.append(f)
    return G