Exemplo n.º 1
0
 def new_xgcd(p,q):
     if(q == 0):
         return p, parent.one(), parent.zero()
     aux_den,_,_ = xgcd(p.denominator(),q.denominator())
     p *= aux_den; q *= aux_den
     if(p.denominator() == 1 and q.denominator() == 1):
         g,P,Q = xgcd(p.numerator(),q.numerator())
         return g, aux_den*parent(P), aux_den*parent(Q)
     m,r = new_euclidean(p,q)
     if(self.is_zero(r)):
         return q, parent.zero(), parent.one()
     if(r.is_unit()):
         return parent.one(), aux_den*parent(1/r), aux_den*parent(-m/r)
     g, P, Q = new_xgcd(q,r)
     return (g, aux_den*Q, aux_den*(P-m*Q))
Exemplo n.º 2
0
    def set_peripheral_info(self):
        G = self.manifold.fundamental_group()
        phi = MapToFreeAbelianization(G)
        m, l = [phi(w)[0] for w in G.peripheral_curves()[0]]
        if m < 0:
            m, l = -m, -l
        self.m_abelian, self.l_abelian = m, l
        # Same as index of homological meridian in H_1(M)_free
        self.l_order = gcd(m, l)

        # We also want to be able to view things from a more homologically
        # natural point of view.

        hom_l = self.manifold.homological_longitude()
        if abs(hom_l[1]) == 1:
            hom_m = (1, 0)
        else:
            a, b = xgcd(*hom_l)[1:]
            hom_m = (b, -a)
            M = self.manifold.copy()
            M.set_peripheral_curves([hom_m, hom_l])
            cusp = M.cusp_info(0).shape
            assert abs(1 + cusp) > 1 - 1e-10 and abs(1 - cusp) > 1 - 1e-10
        self.hom_m = hom_m
        self.hom_l = hom_l
        self.hom_m_abelian = abs(self.m_abelian * hom_m[0] +
                                 self.l_abelian * hom_m[1])
        self.change_trans_to_hom_framing = matrix([hom_m, hom_l])

        # Moreover, we store two special copies of the meridian for
        # later use.

        self.special_meridians = meridians_fixing_infinity_and_zero(
            self.manifold)
Exemplo n.º 3
0
def attack(n, e1, c1, e2, c2):
    """
    Recovers the plaintext from two ciphertexts, encrypted using the same modulus and different public exponents.
    :param n: the common modulus
    :param e1: the first public exponent
    :param c1: the ciphertext of the first encryption
    :param e2: the second public exponent
    :param c2: the ciphertext of the second encryption
    :return: the plaintext
    """
    _, u, v = xgcd(e1, e2)
    p1 = pow(c1, u, n) if u > 0 else pow(pow(c1, -1, n), -u, n)
    p2 = pow(c2, v, n) if v > 0 else pow(pow(c2, -1, n), -v, n)
    return p1 * p2 % n
def HJ_path(s1, s2):
    r""" Return the Hirzebruch-Jung path from s1 to s2.

    INPUT:

    - ``s1``, ``s2`` -- rational number such that `s_1 > s_2`

    OUTPUT: a list of pairs `(m_0, d_0),\ldots,(m_r,d_r)` such that

    .. MATH::

        s_1 > \frac{m_1}{d_1} > \ldots > \frac{m_r}{d_r} = s_2

    and

    .. MATH::

        m_i d_{i+1} - m_{i+1} d_i = 1, \quad i=1,..,r-1.

    """
    from sage.all import xgcd, ceil, floor
    assert s1 > s2, "s1 must be larger than s2"
    m1 = s1.numerator()
    d1 = s1.denominator()
    m2 = s2.numerator()
    d2 = s2.denominator()
    path = [(m1, d1)]
    while m1 / d1 > s2:
        _, x, y = xgcd(m1, d1)
        if d1 * m2 - m1 * d2 > 0:
            k = ceil((x * m2 + y * d2) / (d1 * m2 - m1 * d2))
        else:
            k = floor((x * m2 + y * d2) / (d1 * m2 - m1 * d2))
        m = -y - k * m1
        d = x - k * d1
        path.append((m, d))
        m1, d1 = m, d

    # these test are for debugging only, and should be unnecessary by now
    assert path[0][0] / path[0][1] == s1, "first entry not correct: {}".format(
        path)
    assert path[-1][0] / path[-1][
        1] == s2, "last entry not correct: {}".format(path)
    assert all([
        path[i][0] * path[i + 1][1] - path[i + 1][0] * path[i][1] == 1
        for i in range(len(path) - 1)
    ]), "determinant condition wrong: {}".format(path)

    return path
Exemplo n.º 5
0
def rsa_keys():
    prime1, prime2 = gen_prime(), gen_prime()  # or 65537
    modulus = prime1 * prime2
    phi = (prime1 - 1) * (prime2 - 1)
    # public_exponent = 65537
    public_exponent = sympy.randprime(65537, phi)
    private_exponent = int(sage_all.xgcd(public_exponent,
                                         phi)[1])  # to decrypt
    public_key = [public_exponent, modulus]
    private_key = [private_exponent, modulus]
    json_key("public", public_key)
    json_key("private", private_key)
    # save_key("public", public_key)
    # save_key("private", private_key)
    return public_key, private_key
Exemplo n.º 6
0
def bezout(q, m):
    r"""Returns :math:`u,v` such that :math:`uq+mv=1`.

    Parameters
    ----------
    q,m : integer
        Two coprime integers with :math:`q > 0`.

    Returns
    -------
    tuple of integers

    """
    if q == 1:
        return (1, 0)
    g, u, v = xgcd(q, -m)
    return (u, v)
Exemplo n.º 7
0
def bezout(q,m):
    r"""Returns :math:`u,v` such that :math:`uq+mv=1`.

    Parameters
    ----------
    q,m : integer
        Two coprime integers with :math:`q > 0`.

    Returns
    -------
    tuple of integers

    """
    if q == 1:
        return (1,0)
    g,u,v = xgcd(q,-m)
    return (u,v)
Exemplo n.º 8
0
def zero_lifted_holonomy(manifold, m, l, f):
    """
    Given a closed manifold and any log of the holonomy of the meridian and
    longitude, adjust logs by multiplies of f pi i such that the peripheral
    curves goes to 0.
    """

    CIF = m.parent()
    RIF = CIF.real_field()
    multiple_of_pi = RIF(f*pi)

    # (m_fill, l_fill) Dehn-filling
    m_fill, l_fill = [int(x) for x in manifold.cusp_info()[0]['filling']]

    # Compute what the peripheral curves goes to right now
    p_interval = (m_fill * m + l_fill * l).imag() / multiple_of_pi
    is_int, p = p_interval.is_int()

    if not is_int:
        raise Exception(
            "Expected multiple of %d * pi * i (increase precision?)" % f)

    if p == 0:
        # Nothing to do
        return m, l

    # Compute by what multiple of 2 pi i to adjust
    g, a, b = xgcd(m_fill, l_fill)
    m -= p * a * multiple_of_pi * sage.all.I
    l -= p * b * multiple_of_pi * sage.all.I

    # For sanity, double check that we compute it right.
    p_interval = (m_fill * m + l_fill * l).imag() / multiple_of_pi
    is_int, p = p_interval.is_int()

    if not is_int:
        raise Exception(
            "Expected multiple of %d * pi * i (increase precision?)" % f)

    if p != 0:
        # Nothing to do
        raise Exception("Expected 0")

    return m, l

###############################################################################
# Math functions
###############################################################################

set_seed(SEED + 101)
X = [random.randint(-1000, 1000) for _ in range(20)] + [random.randint(-1000, 1_000_000_000) for _ in range(20)]
Y = [random.randint(-1000, 1000) for _ in range(20)] + [random.randint(-1000, 1_000_000_000) for _ in range(20)]
D = [0,]*len(X)
S = [0,]*len(X)
T = [0,]*len(X)
for i in range(len(X)):
    x = X[i]
    y = Y[i]
    d, s, t = xgcd(x, y)
    D[i] = int(d)
    S[i] = int(s)
    T[i] = int(t)
d = {"X": X, "Y": Y, "D": D, "S": S, "T": T}
save_pickle(d, FOLDER, "egcd.pkl")

set_seed(SEED + 102)
X = [[random.randint(-1000, 1000) for _ in range(random.randint(2, 6))] for _ in range(20)] + [[random.randint(-1000, 1_000_000) for _ in range(random.randint(2, 6))] for _ in range(20)]
Z = [0,]*len(X)
for i in range(len(X)):
    x = X[i]
    z = lcm(x)
    Z[i] = int(z)
d = {"X": X, "Z": Z}
save_pickle(d, FOLDER, "lcm.pkl")
Exemplo n.º 10
0
def make_luts(field, sub_folder, seed, sparse=False):
    global FIELD, RING
    print(f"Making LUTs for {field}")

    ###############################################################################
    # Finite field arithmetic
    ###############################################################################
    folder = os.path.join(PATH, "fields", "data", sub_folder)
    if os.path.exists(folder):
        shutil.rmtree(folder)
    os.mkdir(folder)

    FIELD = field
    RING = PolynomialRing(field, names="x")
    characteristic = int(field.characteristic())
    order = int(field.order())
    dtype = np.int64 if order <= np.iinfo(np.int64).max else object
    alpha = field.primitive_element()
    # assert field.gen() == field.multiplicative_generator()

    d = {
        "characteristic": int(field.characteristic()),
        "degree": int(field.degree()),
        "order": int(field.order()),
        "primitive_element": I(field.primitive_element()),
        "irreducible_poly": [int(c) for c in field.modulus().list()[::-1]]
    }
    save_json(d, folder, "properties.json", indent=True)

    set_seed(seed + 1)
    X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j]) + F(YY[i, j]))
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "add.pkl")

    set_seed(seed + 2)
    X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j]) - F(YY[i, j]))
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "subtract.pkl")

    set_seed(seed + 3)
    X, Y, XX, YY, ZZ = io_2d(0, order, 0, order, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j]) * F(YY[i, j]))
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "multiply.pkl")

    set_seed(seed + 4)
    X, Y, XX, YY, ZZ = io_2d(0, order, -order - 2, order + 3, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j]) * YY[i, j])
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "scalar_multiply.pkl")

    set_seed(seed + 5)
    X, Y, XX, YY, ZZ = io_2d(0, order, 1, order, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j]) / F(YY[i, j]))
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "divide.pkl")

    set_seed(seed + 6)
    X, Z = io_1d(0, order, sparse=sparse)
    for i in range(X.shape[0]):
        Z[i] = I(-F(X[i]))
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "additive_inverse.pkl")

    set_seed(seed + 7)
    X, Z = io_1d(1, order, sparse=sparse)
    for i in range(X.shape[0]):
        Z[i] = I(1 / F(X[i]))
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "multiplicative_inverse.pkl")

    set_seed(seed + 8)
    X, Y, XX, YY, ZZ = io_2d(1, order, -order - 2, order + 3, sparse=sparse)
    for i in range(ZZ.shape[0]):
        for j in range(ZZ.shape[1]):
            ZZ[i, j] = I(F(XX[i, j])**YY[i, j])
    d = {"X": X, "Y": Y, "Z": ZZ}
    save_pickle(d, folder, "power.pkl")

    set_seed(seed + 9)
    X, Z = io_1d(1, order, sparse=sparse)
    for i in range(Z.shape[0]):
        try:
            Z[i] = I(field.fetch_int(X[i]).log(alpha))
        except:
            Z[i] = I(log(F(X[i]), alpha))
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "log.pkl")

    set_seed(seed + 10)
    X, Z = io_1d(0, order, sparse=sparse)
    for i in range(X.shape[0]):
        Z[i] = int(F(X[i]).additive_order())
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "additive_order.pkl")

    set_seed(seed + 11)
    X, Z = io_1d(1, order, sparse=sparse)
    for i in range(X.shape[0]):
        Z[i] = int(F(X[i]).multiplicative_order())
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "multiplicative_order.pkl")

    set_seed(seed + 12)
    X, _ = io_1d(0, order, sparse=sparse)
    Z = []
    for i in range(len(X)):
        x = F(X[i])
        p = x.charpoly()
        z = poly_to_list(p)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "characteristic_poly_element.pkl")

    set_seed(seed + 13)
    shapes = [(2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]
    X = []
    Z = []
    for i in range(len(shapes)):
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        p = x.charpoly()
        z = poly_to_list(p)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "characteristic_poly_matrix.pkl")

    set_seed(seed + 14)
    X, _ = io_1d(0, order, sparse=sparse)
    Z = []
    for i in range(len(X)):
        x = F(X[i])
        p = x.minpoly()
        z = poly_to_list(p)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "minimal_poly_element.pkl")

    # set_seed(seed + 15)
    # shapes = [(2,2), (3,3), (4,4), (5,5), (6,6)]
    # X = []
    # Z = []
    # for i in range(len(shapes)):
    #     x = randint_matrix(0, order, shapes[i])
    #     X.append(x)
    #     x = matrix(FIELD, [[F(e) for e in row] for row in x])
    #     p = x.minpoly()
    #     z = np.array([I(e) for e in p.list()[::-1]], dtype=dtype).tolist()
    #     z = z if z != [] else [0]
    #     Z.append(z)
    # d = {"X": X, "Z": Z}
    # save_pickle(d, folder, "minimal_poly_matrix.pkl")

    set_seed(seed + 16)
    X, Z = io_1d(0, order, sparse=sparse)
    for i in range(X.shape[0]):
        z = F(X[i]).trace()
        Z[i] = int(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "field_trace.pkl")

    set_seed(seed + 17)
    X, Z = io_1d(0, order, sparse=sparse)
    for i in range(X.shape[0]):
        z = F(X[i]).norm()
        Z[i] = int(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "field_norm.pkl")

    ###############################################################################
    # Linear algebra
    ###############################################################################

    set_seed(seed + 201)
    X_shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4)]
    Y_shapes = [(2, 2), (3, 3), (2, 4), (3, 3), (4, 5)]
    X = []
    Y = []
    Z = []
    for i in range(len(shapes)):
        x = randint_matrix(0, order, X_shapes[i])
        y = randint_matrix(0, order, Y_shapes[i])
        X.append(x)
        Y.append(y)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        y = matrix(FIELD, [[F(e) for e in row] for row in y])
        z = x * y
        z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "matrix_multiply.pkl")

    set_seed(seed + 202)
    shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4)]
    X = []
    Z = []
    for i in range(len(shapes)):
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = x.rref()
        z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "row_reduce.pkl")

    set_seed(seed + 203)
    shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4), (4, 3), (4, 4), (4, 5),
              (5, 4), (5, 5), (5, 6), (6, 5), (6, 6)]
    X = []
    L = []
    U = []
    for i in range(len(shapes)):
        while True:
            # Ensure X has a PLU decomposition with P = I, which means it has an LU decomposition
            x = randint_matrix(0, order, shapes[i])
            x_orig = x.copy()
            x = matrix(FIELD, [[F(e) for e in row] for row in x])
            p, l, u = x.LU()
            if p == matrix.identity(FIELD, shapes[i][0]):
                break
        X.append(x_orig)
        l = np.array([[I(e) for e in row] for row in l], dtype)
        u = np.array([[I(e) for e in row] for row in u], dtype)
        L.append(l)
        U.append(u)
    d = {"X": X, "L": L, "U": U}
    save_pickle(d, folder, "lu_decompose.pkl")

    set_seed(seed + 204)
    shapes = [(2, 2), (2, 3), (3, 2), (3, 3), (3, 4), (4, 3), (4, 4), (4, 5),
              (5, 4), (5, 5), (5, 6), (6, 5), (6, 6)]
    X = []
    L = []
    U = []
    P = []
    for i in range(len(shapes)):
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        p, l, u = x.LU()
        p = np.array([[I(e) for e in row] for row in p], dtype)
        l = np.array([[I(e) for e in row] for row in l], dtype)
        u = np.array([[I(e) for e in row] for row in u], dtype)
        P.append(p)
        L.append(l)
        U.append(u)
    d = {"X": X, "P": P, "L": L, "U": U}
    save_pickle(d, folder, "plu_decompose.pkl")

    set_seed(seed + 205)
    shapes = [(2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]
    X = []
    Z = []
    for i in range(len(shapes)):
        while True:
            x = randint_matrix(0, order, shapes[i])
            x_orig = x.copy()
            x = matrix(FIELD, [[F(e) for e in row] for row in x])
            if x.rank() == shapes[i][0]:
                break
        X.append(x_orig)
        z = x.inverse()
        z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "matrix_inverse.pkl")

    set_seed(seed + 206)
    shapes = [(2, 2), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), (4, 4), (4, 4),
              (4, 4), (5, 5), (5, 5), (5, 5), (6, 6), (6, 6), (6, 6)]
    X = []
    Z = []
    for i in range(len(shapes)):
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = I(x.determinant())
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "matrix_determinant.pkl")

    set_seed(seed + 207)
    shapes = [(2, 2), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), (4, 4), (4, 4),
              (4, 4), (5, 5), (5, 5), (5, 5), (6, 6), (6, 6), (6, 6)]
    X = []
    Y = []
    Z = []
    for i in range(len(shapes)):
        while True:
            x = randint_matrix(0, order, shapes[i])
            x_orig = x.copy()
            x = matrix(FIELD, [[F(e) for e in row] for row in x])
            if x.rank() == shapes[i][0]:
                break
        X.append(x_orig)
        y = randint_matrix(0, order, shapes[i][1])  # 1-D vector
        Y.append(y)
        y = vector(FIELD, [F(e) for e in y])
        z = x.solve_right(y)
        z = np.array([I(e) for e in z], dtype)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "matrix_solve.pkl")

    set_seed(seed + 208)
    shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)]
    X = []
    Z = []
    for i in range(len(shapes)):
        deg = shapes[i][1]  # The degree of the vector space

        # Random matrix
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = x.row_space()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)

        # Reduce the row space by 1 by copying the 0th row to the jth row
        for j in range(1, shapes[i][0]):
            x = copy(x)
            x[j, :] = F(random.randint(0, order - 1)) * x[0, :]

            z = x.row_space()
            if z.dimension() == 0:
                z = randint_matrix(0, 1, (0, deg))
            else:
                z = z.basis_matrix()
                z = np.array([[I(e) for e in row] for row in z], dtype)
            X.append(np.array([[I(e) for e in row] for row in x], dtype))
            Z.append(z)

        # Zero matrix
        x = copy(x)
        x[:] = F(0)
        z = x.row_space()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        X.append(np.array([[I(e) for e in row] for row in x], dtype))
        Z.append(z)

    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "row_space.pkl")

    set_seed(seed + 209)
    shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)]
    X = []
    Z = []
    for i in range(len(shapes)):
        deg = shapes[i][0]  # The degree of the vector space

        # Random matrix
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = x.column_space()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)

        # Reduce the column space by 1 by copying the 0th column to the jth column
        for j in range(1, shapes[i][1]):
            x = copy(x)
            x[:, j] = F(random.randint(0, order - 1)) * x[:, 0]

            z = x.column_space()
            if z.dimension() == 0:
                z = randint_matrix(0, 1, (0, deg))
            else:
                z = z.basis_matrix()
                z = np.array([[I(e) for e in row] for row in z], dtype)
            X.append(np.array([[I(e) for e in row] for row in x], dtype))
            Z.append(z)

        # Zero matrix
        x = copy(x)
        x[:] = F(0)
        z = x.column_space()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        X.append(np.array([[I(e) for e in row] for row in x], dtype))
        Z.append(z)

    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "column_space.pkl")

    set_seed(seed + 210)
    shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)]
    X = []
    Z = []
    for i in range(len(shapes)):
        deg = shapes[i][0]  # The degree of the vector space

        # Random matrix
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = x.left_kernel()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)

        # Reduce the left null space by 1 by copying the 0th row to the jth row
        for j in range(1, shapes[i][0]):
            x = copy(x)
            x[j, :] = F(random.randint(0, order - 1)) * x[0, :]

            z = x.left_kernel()
            if z.dimension() == 0:
                z = randint_matrix(0, 1, (0, deg))
            else:
                z = z.basis_matrix()
                z = np.array([[I(e) for e in row] for row in z], dtype)
            X.append(np.array([[I(e) for e in row] for row in x], dtype))
            Z.append(z)

        # Zero matrix
        x = copy(x)
        x[:] = F(0)
        z = x.left_kernel()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        X.append(np.array([[I(e) for e in row] for row in x], dtype))
        Z.append(z)

    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "left_null_space.pkl")

    set_seed(seed + 211)
    shapes = [(2, 2), (2, 3), (2, 4), (3, 2), (4, 2), (3, 3)]
    X = []
    Z = []
    for i in range(len(shapes)):
        deg = shapes[i][1]  # The degree of the vector space

        # Random matrix
        x = randint_matrix(0, order, shapes[i])
        X.append(x)
        x = matrix(FIELD, [[F(e) for e in row] for row in x])
        z = x.right_kernel()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)

        # Reduce the null space by 1 by copying the 0th column to the jth column
        for j in range(1, shapes[i][1]):
            x = copy(x)
            x[:, j] = F(random.randint(0, order - 1)) * x[:, 0]

            z = x.right_kernel()
            if z.dimension() == 0:
                z = randint_matrix(0, 1, (0, deg))
            else:
                z = z.basis_matrix()
                z = np.array([[I(e) for e in row] for row in z], dtype)
            X.append(np.array([[I(e) for e in row] for row in x], dtype))
            Z.append(z)

        # Zero matrix
        x = copy(x)
        x[:] = F(0)
        z = x.right_kernel()
        if z.dimension() == 0:
            z = randint_matrix(0, 1, (0, deg))
        else:
            z = z.basis_matrix()
            z = np.array([[I(e) for e in row] for row in z], dtype)
        X.append(np.array([[I(e) for e in row] for row in x], dtype))
        Z.append(z)

    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "null_space.pkl")

    ###############################################################################
    # Polynomial arithmetic
    ###############################################################################
    folder = os.path.join(PATH, "polys", "data", sub_folder)
    if os.path.exists(folder):
        shutil.rmtree(folder)
    os.mkdir(folder)

    MIN_COEFFS = 1
    MAX_COEFFS = 12

    set_seed(seed + 101)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = list_to_poly(Y[i])
        z = x + y
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "add.pkl")

    set_seed(seed + 102)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = list_to_poly(Y[i])
        z = x - y
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "subtract.pkl")

    set_seed(seed + 103)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = list_to_poly(Y[i])
        z = x * y
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "multiply.pkl")

    set_seed(seed + 104)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random.randint(1, 2 * characteristic) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = Y[i]
        z = x * y
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "scalar_multiply.pkl")

    set_seed(seed + 105)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    # Add some specific polynomial types
    X.append([0]), Y.append(random_coeffs(0, order, MIN_COEFFS,
                                          MAX_COEFFS))  # 0 / y
    X.append(random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS // 2)), Y.append(
        random_coeffs(0, order, MAX_COEFFS // 2,
                      MAX_COEFFS))  # x / y with x.degree < y.degree
    X.append(random_coeffs(0, order, 2, MAX_COEFFS)), Y.append(
        random_coeffs(0, order, 1, 2))  # x / y with y.degree = 0
    Q = []
    R = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = list_to_poly(Y[i])
        q = x // y
        r = x % y
        q = poly_to_list(q)
        Q.append(q)
        r = poly_to_list(r)
        R.append(r)
    d = {"X": X, "Y": Y, "Q": Q, "R": R}
    save_pickle(d, folder, "divmod.pkl")

    set_seed(seed + 106)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(4)]
    X.append(random_coeffs(0, order, 1, 2))
    Y = [0, 1, 2, 3]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        ZZ = []
        for j in range(len(Y)):
            y = Y[j]
            z = x**y
            z = poly_to_list(z)
            ZZ.append(z)
        Z.append(ZZ)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "power.pkl")

    set_seed(seed + 107)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = arange(0, order, sparse=sparse)
    Z = np.array(np.zeros((len(X), len(Y))), dtype=dtype)
    for i in range(len(X)):
        for j in range(len(Y)):
            x = list_to_poly(X[i])
            y = F(Y[j])
            z = x(y)
            Z[i, j] = I(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "evaluate.pkl")

    set_seed(seed + 108)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [randint_matrix(0, order, (2, 2)) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = matrix(FIELD, [[F(e) for e in row] for row in Y[i]])
        z = x(y)
        z = np.array([[I(e) for e in row] for row in z], dtype)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "evaluate_matrix.pkl")

    ###############################################################################
    # Polynomial arithmetic methods
    ###############################################################################

    set_seed(seed + 301)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        z = x.reverse()
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "reverse.pkl")

    set_seed(seed + 302)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    R = []
    M = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        roots = x.roots()
        RR, MM = [], []
        for root in roots:
            r = root[0]
            m = root[1]
            RR.append(I(r))
            MM.append(int(m))
        idxs = np.argsort(RR)  # Sort by ascending roots
        RR = (np.array(RR, dtype=dtype)[idxs]).tolist()
        MM = (np.array(MM, dtype=dtype)[idxs]).tolist()
        R.append(RR)
        M.append(MM)
    d = {"X": X, "R": R, "M": M}
    save_pickle(d, folder, "roots.pkl")

    set_seed(seed + 303)
    X = [
        random_coeffs(0, order, 2 * FIELD.degree(), 6 * FIELD.degree())
        for i in range(20)
    ]
    Y = [
        1,
    ] * 10 + [random.randint(2,
                             FIELD.degree() + 1) for i in range(10)]
    Z = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        z = x.derivative(Y[i])
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "derivative.pkl")

    ###############################################################################
    # Polynomial arithmetic functions
    ###############################################################################

    set_seed(seed + 401)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Y = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    D = []
    S = []
    T = []
    for i in range(len(X)):
        x = list_to_poly(X[i])
        y = list_to_poly(Y[i])
        d, s, t = xgcd(x, y)
        d = poly_to_list(d)
        s = poly_to_list(s)
        t = poly_to_list(t)
        D.append(d)
        S.append(s)
        T.append(t)
    d = {"X": X, "Y": Y, "D": D, "S": S, "T": T}
    save_pickle(d, folder, "egcd.pkl")

    set_seed(seed + 402)
    X = []
    Z = []
    for i in range(20):
        XX = [
            random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS)
            for i in range(random.randint(2, 5))
        ]
        X.append(XX)
        xx = [list_to_poly(XXi) for XXi in XX]
        z = lcm(xx)
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "lcm.pkl")

    set_seed(seed + 403)
    X = []
    Z = []
    for i in range(20):
        XX = [
            random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS)
            for i in range(random.randint(2, 5))
        ]
        X.append(XX)
        xx = [list_to_poly(XXi) for XXi in XX]
        z = prod(xx)
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "prod.pkl")

    set_seed(seed + 404)
    X = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    E = [random.randint(2, 10) for i in range(20)]
    M = [random_coeffs(0, order, MIN_COEFFS, MAX_COEFFS) for i in range(20)]
    Z = []
    for i in range(20):
        x = list_to_poly(X[i])
        e = E[i]
        m = list_to_poly(M[i])
        z = (x**e) % m
        z = poly_to_list(z)
        Z.append(z)
    d = {"X": X, "E": E, "M": M, "Z": Z}
    save_pickle(d, folder, "modular_power.pkl")

    set_seed(seed + 405)
    X = [
        0,
    ] * 20  # The remainder
    Y = [
        0,
    ] * 20  # The modulus
    Z = [
        0,
    ] * 20  # The solution
    for i in range(20):
        n = random.randint(2, 4)  # The number of polynomials
        x, y = [], []
        for j in range(n):
            d = random.randint(3, 5)
            x.append(random_coeffs(0, order, d, d + 1))
            y.append(random_coeffs(
                0, order, d + 1, d +
                2))  # Ensure modulus degree is greater than remainder degree
        X[i] = x
        Y[i] = y
        try:
            x = [list_to_poly(xx) for xx in x]
            y = [list_to_poly(yy) for yy in y]
            z = crt(x, y)
            Z[i] = poly_to_list(z)
        except:
            Z[i] = None
    d = {"X": X, "Y": Y, "Z": Z}
    save_pickle(d, folder, "crt.pkl")

    ###############################################################################
    # Special polynomials
    ###############################################################################

    set_seed(seed + 501)
    X = [random_coeffs(0, order, 1, 6) for _ in range(20)]
    Z = [
        False,
    ] * len(X)
    for i in range(len(X)):
        if random.choice(["one", "other"]) == "one":
            X[i][0] = 1
        x = list_to_poly(X[i])
        z = x.is_monic()
        Z[i] = bool(z)
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "is_monic.pkl")

    set_seed(seed + 502)
    IS = []
    IS_NOT = []
    if order <= 2**16:
        while len(IS) < 10:
            x = random_coeffs(0, order, 1, 6)
            f = list_to_poly(x)
            if f.is_irreducible():
                IS.append(x)
        while len(IS_NOT) < 10:
            x = random_coeffs(0, order, 1, 6)
            f = list_to_poly(x)
            if not f.is_irreducible():
                IS_NOT.append(x)
    d = {"IS": IS, "IS_NOT": IS_NOT}
    save_pickle(d, folder, "is_irreducible.pkl")

    set_seed(seed + 503)
    IS = []
    IS_NOT = []
    if order <= 2**16:
        while len(IS) < 10:
            x = random_coeffs(0, order, 1, 6)
            f = list_to_poly(x)
            # f = f / f.coefficients()[-1]  # Make monic
            # assert f.is_monic()
            if f.degree() == 1 and f.coefficients(sparse=False)[0] == 0:
                continue  # For some reason `is_primitive()` crashes on f(x) = a*x
            if not f.is_irreducible():
                continue  # Want to find an irreducible polynomial that is also primitive
            if f.is_primitive():
                IS.append(x)
        while len(IS_NOT) < 10:
            x = random_coeffs(0, order, 1, 6)
            f = list_to_poly(x)
            # f = f / f.coefficients()[-1]  # Make monic
            # assert f.is_monic()
            if f.degree() == 1 and f.coefficients(sparse=False)[0] == 0:
                continue  # For some reason `is_primitive()` crashes on f(x) = a*x
            if not f.is_irreducible():
                continue  # Want to find an irreducible polynomial that is not primitive
            if not f.is_primitive():
                IS_NOT.append(x)
    d = {"IS": IS, "IS_NOT": IS_NOT}
    save_pickle(d, folder, "is_primitive.pkl")

    set_seed(seed + 504)
    if order <= 2**16:
        X = [random_coeffs(0, order, 1, 6) for _ in range(20)]
        Z = [
            False,
        ] * len(X)
        for i in range(len(X)):
            x = list_to_poly(X[i])
            z = x.is_squarefree()
            Z[i] = bool(z)
    else:
        X = []
        Z = []
    d = {"X": X, "Z": Z}
    save_pickle(d, folder, "is_square_free.pkl")