Exemple #1
0
    def decomposition(self, B, verbose=False):
        """
        Return Hecke decomposition of self using Hecke operators T_p
        coprime to the level with norm(p) <= B.
        """

        # TODO: rewrite to use primes_of_bounded_norm so that we
        # iterate through primes ordered by *norm*, which is
        # potentially vastly faster.  Delete these functions
        # involving characteristic!
        
        p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
        T = self.hecke_matrix(p)
        D = T.decomposition()
        while len([X for X in D if not X[1]]) > 0:
            p = next_prime_of_characteristic_coprime_to(p, self.level())
            if p.norm() > B:
                break
            if verbose: print p.norm()
            T = self.hecke_matrix(p)
            D2 = []
            for X in D:
                if X[1]:
                    D2.append(X)
                else:
                    if verbose: print T.restrict(X[0]).fcp()
                    for Z in T.decomposition_of_subspace(X[0]):
                        D2.append(Z)
            D = D2
        D = [self.subspace(X[0]) for X in D]
        D.sort()
        S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
        return S
Exemple #2
0
 def new_decomposition(self, verbose=False):
     """
     Return complete irreducible Hecke decomposition of new subspace of self.
     """
     V = self.degeneracy_matrix().kernel()
     p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
     T = self.hecke_matrix(p)
     D = T.decomposition_of_subspace(V)
     while len([X for X in D if not X[1]]) > 0:
         p = next_prime_of_characteristic_coprime_to(p, self.level())
         if verbose: print p.norm()
         T = self.hecke_matrix(p)
         D2 = []
         for X in D:
             if X[1]:
                 D2.append(X)
             else:
                 if verbose: print T.restrict(X[0]).fcp()
                 for Z in T.decomposition_of_subspace(X[0]):
                     D2.append(Z)
         D = D2
     D = [self.subspace(X[0]) for X in D]
     D.sort()
     S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
     return S
Exemple #3
0
 def new_decomposition(self, verbose=False):
     """
     Return complete irreducible Hecke decomposition of new subspace of self.
     """
     V = self.degeneracy_matrix().kernel()
     p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
     T = self.hecke_matrix(p)
     D = T.decomposition_of_subspace(V)
     while len([X for X in D if not X[1]]) > 0:
         p = next_prime_of_characteristic_coprime_to(p, self.level())
         if verbose: print p.norm()
         T = self.hecke_matrix(p)
         D2 = []
         for X in D:
             if X[1]:
                 D2.append(X)
             else:
                 if verbose: print T.restrict(X[0]).fcp()
                 for Z in T.decomposition_of_subspace(X[0]):
                     D2.append(Z)
         D = D2
     D = [self.subspace(X[0]) for X in D]
     D.sort()
     S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
     return S
Exemple #4
0
    def decomposition(self, B, verbose=False):
        """
        Return Hecke decomposition of self using Hecke operators T_p
        coprime to the level with norm(p) <= B.
        """

        # TODO: rewrite to use primes_of_bounded_norm so that we
        # iterate through primes ordered by *norm*, which is
        # potentially vastly faster.  Delete these functions
        # involving characteristic!
        
        p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
        T = self.hecke_matrix(p)
        D = T.decomposition()
        while len([X for X in D if not X[1]]) > 0:
            p = next_prime_of_characteristic_coprime_to(p, self.level())
            if p.norm() > B:
                break
            if verbose: print p.norm()
            T = self.hecke_matrix(p)
            D2 = []
            for X in D:
                if X[1]:
                    D2.append(X)
                else:
                    if verbose: print T.restrict(X[0]).fcp()
                    for Z in T.decomposition_of_subspace(X[0]):
                        D2.append(Z)
            D = D2
        D = [self.subspace(X[0]) for X in D]
        D.sort()
        S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
        return S
def ideals_of_norm(v):
    """
    INPUT:

    - `v` -- integer >= 2, or list of integers >= 2

    OUTPUT:

    - list of ideals of all ideals that have norm in v that is >= 2.

    EXAMPLES::

        sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm(4)
        [Fractional ideal (2)]
        sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm([9,11])
        [Fractional ideal (3), Fractional ideal (-3*a + 1), Fractional ideal (-3*a + 2)]
        sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm([4*5])
        [Fractional ideal (-4*a + 2)]
        sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm(4*5)
        [Fractional ideal (-4*a + 2)]        
    """
    try:
        v = list(v)
    except TypeError:
        v = [Integer(v)]
    z = F.ideals_of_bdd_norm(max(v))
    return sum([z[n] for n in v if n > 1], [])
Exemple #6
0
def find_mod2pow_splitting(i):
    P = F.primes_above(2)[0]
    if i == 1:
        R = ResidueRing(P, 1)
        M = MatrixSpace(R, 2)
        return [M(matrix_lift(a).list()) for a in find_mod2_splitting()]

    R = ResidueRing(P, i)
    M = MatrixSpace(R, 2)
    # arbitrary lift
    wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i - 1)]

    # Find lifts of wbar[1] and wbar[2] that have square -1
    k = P.residue_field()
    Mk = MatrixSpace(k, 2)

    t = 2**(i - 1)
    s = M(t)

    L = []
    for j in [1, 2]:
        C = Mk(matrix_lift(wbar[j]**2 + M(1)) / t)
        A = Mk(matrix_lift(wbar[j]))
        # Find all matrices B in Mk such that AB+BA=C.
        L.append([
            wbar[j] + s * M(matrix_lift(B)) for B in Mk if A * B + B * A == C
        ])

    g = M(F.gen())
    t = M(t)
    two = M(2)
    ginv = M(F.gen()**(-1))
    for w1 in L[0]:
        for w2 in L[1]:
            w0 = ginv * (two * g * wbar[3] - w1 - w2 - w1 * w2)
            w3 = g * w0 + w2 * w1
            if w0 * w0 != w0 - M(1):
                continue
            if w3 * w3 != M(-1):
                continue
            return w0, w1, w2, w3

    raise ValueError
Exemple #7
0
def next_prime_not_dividing(P, I):
    while True:
        p = P.smallest_integer()
        if p == 1:
            Q = F.ideal(2)
        elif p % 5 in [2,3]: # inert
            Q = F.primes_above(next_prime(p))[0]
        elif p == 5:
            Q = F.ideal(7)
        else: # p split
            A = F.primes_above(p)
            if A[0] == P:
                Q = A[1]
            else:
                Q = F.primes_above(next_prime(p))[0]
        if not Q.divides(I):
            return Q
        else:
            P = Q # try again
Exemple #8
0
def next_prime_not_dividing(P, I):
    while True:
        p = P.smallest_integer()
        if p == 1:
            Q = F.ideal(2)
        elif p % 5 in [2,3]: # inert
            Q = F.primes_above(next_prime(p))[0]
        elif p == 5:
            Q = F.ideal(7)
        else: # p split
            A = F.primes_above(p)
            if A[0] == P:
                Q = A[1]
            else:
                Q = F.primes_above(next_prime(p))[0]
        if not Q.divides(I):
            return Q
        else:
            P = Q # try again
def find_mod2pow_splitting(i):
    P = F.primes_above(2)[0]
    if i == 1:
        R = ResidueRing(P, 1)
        M = MatrixSpace(R, 2)
        return [M(matrix_lift(a).list()) for a in find_mod2_splitting()]

    R = ResidueRing(P, i)
    M = MatrixSpace(R, 2)
    # arbitrary lift
    wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i - 1)]

    # Find lifts of wbar[1] and wbar[2] that have square -1
    k = P.residue_field()
    Mk = MatrixSpace(k, 2)

    t = 2 ** (i - 1)
    s = M(t)

    L = []
    for j in [1, 2]:
        C = Mk(matrix_lift(wbar[j] ** 2 + M(1)) / t)
        A = Mk(matrix_lift(wbar[j]))
        # Find all matrices B in Mk such that AB+BA=C.
        L.append([wbar[j] + s * M(matrix_lift(B)) for B in Mk if A * B + B * A == C])

    g = M(F.gen())
    t = M(t)
    two = M(2)
    ginv = M(F.gen() ** (-1))
    for w1 in L[0]:
        for w2 in L[1]:
            w0 = ginv * (two * g * wbar[3] - w1 - w2 - w1 * w2)
            w3 = g * w0 + w2 * w1
            if w0 * w0 != w0 - M(1):
                continue
            if w3 * w3 != M(-1):
                continue
            return w0, w1, w2, w3

    raise ValueError
Exemple #10
0
 def elliptic_curve_factors(self):
     D = [X for X in self.new_decomposition() if X.dimension() == 1]
     # Have to get rid of the Eisenstein factor
     p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
     while True:
         q = p.residue_field().cardinality() + 1
         E = [A for A in D if A.hecke_matrix(p)[0,0] == q]
         if len(E) == 0:
             break
         elif len(E) == 1:
             D = [A for A in D if A != E[0]]
             break
         else:
             p = next_prime_of_characteristic_coprime_to(p, self.level())
     return Sequence([EllipticCurveFactor(X, number) for number, X in enumerate(D)],
                     immutable=True, cr=True, universe=int, check=False)
Exemple #11
0
 def elliptic_curve_factors(self):
     D = [X for X in self.new_decomposition() if X.dimension() == 1]
     # Have to get rid of the Eisenstein factor
     p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
     while True:
         q = p.residue_field().cardinality() + 1
         E = [A for A in D if A.hecke_matrix(p)[0,0] == q]
         if len(E) == 0:
             break
         elif len(E) == 1:
             D = [A for A in D if A != E[0]]
             break
         else:
             p = next_prime_of_characteristic_coprime_to(p, self.level())
     return Sequence([EllipticCurveFactor(X, number) for number, X in enumerate(D)],
                     immutable=True, cr=True, universe=int, check=False)
def find_mod2_splitting():
    """
    Return elements w0, w1, w2, w3 that determine a mod-2 splitting
    of the Hamilton quaternion algebra over Q(sqrt(5)).

    OUTPUT:

    - four 2x2 matrices over the residue field of Q(sqrt(5)) of order 4.
    
    EXAMPLES::

        sage: import sage.modular.hilbert.sqrt5_fast_python
        sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2_splitting(); w
        (
        [       1     abar]  [       0 abar + 1]  [abar + 1     abar]  [1 1]
        [abar + 1        0], [    abar        0], [    abar abar + 1], [0 1]
        )
        sage: w[1]^2, w[2]^2
        (
        [1 0]  [1 0]
        [0 1], [0 1]
        )
        sage: span([vector(z.list()) for z in w]).dimension()
        4
    """
    P = F.primes_above(2)[0]
    k = P.residue_field()
    M = MatrixSpace(k,2)
    V = k**4
    g = k.gen() # image of golden ratio

    m1 = M(-1)
    sqrt_minus_1 = [(w, V(w.list())) for w in M if w*w == m1]
    one = M(1)
    v_one = V(one.list())
    for w1, v1 in sqrt_minus_1:
        for w2, v2  in sqrt_minus_1:
            w0 = (g-1)*(w1+w2 - w1*w2)
            w3 = g*w0 + w2*w1
            if w0*w0 != w0 - 1:
                continue
            if w3*w3 != -1:
                continue
            if V.span([w0.list(),v1,v2,w3.list()]).dimension() == 4:
                return w0, w1, w2, w3
def test_reduced_rep(B=50):
    """
    Unit test: run a simple consistency check that reduced_rep is
    working sensibly.
    
    INPUT:

    - `B` -- positive integer

    OUTPUT:

    - None (assertion raised if something detected wrong)

    EXAMPLES::

        sage: sage.modular.hilbert.sqrt5_tables.test_reduced_rep()
    """
    a = F.gen()
    z = -45 * a + 28
    v = [reduced_rep(a ** i * z) for i in range(-B, B)]
    assert len(set(v)) == 1
def find_mod2_splitting():
    P = F.primes_above(2)[0]
    k = P.residue_field()
    M = MatrixSpace(k, 2)
    V = k ** 4
    g = k.gen()  # image of golden ratio

    m1 = M(-1)
    sqrt_minus_1 = [(w, V(w.list())) for w in M if w * w == m1]
    one = M(1)
    v_one = V(one.list())
    for w1, v1 in sqrt_minus_1:
        for w2, v2 in sqrt_minus_1:
            w0 = (g - 1) * (w1 + w2 - w1 * w2)
            w3 = g * w0 + w2 * w1
            if w0 * w0 != w0 - 1:
                continue
            if w3 * w3 != -1:
                continue
            if V.span([w0.list(), v1, v2, w3.list()]).dimension() == 4:
                return w0, w1, w2, w3
def ideals_of_bounded_norm(B):
    r"""
    Return all ideals in the ring of integers of Q(sqrt(5)) with norm
    bigger than 1 and <= B.
    
    INPUT:

    - `B` -- positive integer

    OUTPUT:

    - list of ideals

    EXAMPLES::

        sage: v = sage.modular.hilbert.sqrt5_tables.ideals_of_bounded_norm(11); v
        [Fractional ideal (2), Fractional ideal (-2*a + 1), Fractional ideal (3), Fractional ideal (-3*a + 1), Fractional ideal (-3*a + 2)]
        sage: [I.norm() for I in v]
        [4, 5, 9, 11, 11]
    """
    return sum([v for n, v in F.ideals_of_bdd_norm(B).iteritems() if n != 1], [])
Exemple #16
0
def find_mod2_splitting():
    P = F.primes_above(2)[0]
    k = P.residue_field()
    M = MatrixSpace(k, 2)
    V = k**4
    g = k.gen()  # image of golden ratio

    m1 = M(-1)
    sqrt_minus_1 = [(w, V(w.list())) for w in M if w * w == m1]
    one = M(1)
    v_one = V(one.list())
    for w1, v1 in sqrt_minus_1:
        for w2, v2 in sqrt_minus_1:
            w0 = (g - 1) * (w1 + w2 - w1 * w2)
            w3 = g * w0 + w2 * w1
            if w0 * w0 != w0 - 1:
                continue
            if w3 * w3 != -1:
                continue
            if V.span([w0.list(), v1, v2, w3.list()]).dimension() == 4:
                return w0, w1, w2, w3
Exemple #17
0
def next_prime_of_characteristic_coprime_to(P, I):
    p = next_prime(P.smallest_integer())
    N = ZZ(I.norm())
    while N%p == 0:
        p = next_prime(p)
    return F.primes_above(p)[0]
def find_mod2pow_splitting(i):
    """
    Return elements w0, w1, w2, w3 that determine a mod-`2^i` splitting
    of the Hamilton quaternion algebra over Q(sqrt(5)).

    INPUT:

    - `i` -- positive integer

    OUTPUT:

    - four 2x2 matrices over a residue ring of Q(sqrt(5)) of characteristic a power of 2
    
    EXAMPLES::

        sage: import sage.modular.hilbert.sqrt5_fast_python
        sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(1); w
        (
        [    1     g]  [    0 1 + g]  [1 + g     g]  [1 1]
        [1 + g     0], [    g     0], [    g 1 + g], [0 1]
        )
        sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(2); w
        (
        [      3 2 + 3*g]  [      0 1 + 3*g]  [3 + 3*g     3*g]
        [  1 + g       2], [      g       0], [    3*g   1 + g],
        [3 + 2*g 3 + 2*g]
        [      2 1 + 2*g]
        )
        sage: w[1]^2
        [3 0]
        [0 3]
        sage: w[2]^2
        [3 0]
        [0 3]
        sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(4)
        sage: w[1]^2
        [15  0]
        [ 0 15]
        sage: w[2]^2
        [15  0]
        [ 0 15]
        sage: w[1], w[2]
        (
        [       0 1 + 15*g]  [15 + 15*g  12 + 7*g]
        [       g        0], [ 4 + 11*g     1 + g]
        )

    TESTS:

    The power must be positive::

        sage: sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(0)
        Traceback (most recent call last):
        ...
        ValueError: i must be positive    
    """
    P = F.primes_above(2)[0]
    i = ZZ(i)
    if i <= 0:
        raise ValueError, "i must be positive"
    
    if i == 1:
        R = ResidueRing(P, 1)
        M = MatrixSpace(R,2)
        return tuple([M(matrix_lift(a).list()) for a in find_mod2_splitting()])

    R = ResidueRing(P, i)
    M = MatrixSpace(R,2)
    # arbitrary lift
    wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i-1)]

    # Find lifts of wbar[1] and wbar[2] that have square -1
    k = P.residue_field()
    Mk = MatrixSpace(k, 2)
    
    t = 2**(i-1)
    s = M(t)

    L = []
    for j in [1,2]:
        C = Mk(matrix_lift(wbar[j]**2 + M(1)) / t)
        A = Mk(matrix_lift(wbar[j]))
        # Find all matrices B in Mk such that AB+BA=C.
        L.append([wbar[j]+s*M(matrix_lift(B)) for B in Mk if A*B + B*A == C])

    g = M(F.gen())
    t = M(t)
    two = M(2)
    ginv = M(F.gen()**(-1))
    for w1 in L[0]:
        for w2 in L[1]:
            w0 = ginv*(two*g*wbar[3] -w1 -w2 - w1*w2)
            w3 = g*w0 + w2*w1
            if w0*w0 != w0 - M(1):
                continue
            if w3*w3 != M(-1):
                continue
            return w0, w1, w2, w3
        
    raise ValueError
Exemple #19
0
def next_prime_of_characteristic_coprime_to(P, I):
    p = next_prime(P.smallest_integer())
    N = ZZ(I.norm())
    while N%p == 0:
        p = next_prime(p)
    return F.primes_above(p)[0]
def dimensions(v, filename=None):
    """
    Compute dimensions of spaces of Hilbert modular forms for all the levels in v.
    The format is:

        Norm   dimension  generator  time

    INPUT:

    - `v` -- list of positive integers
    - ``filename`` -- optional string; if given, output is also written
      to that file (in addition to stdout).

    OUTPUT:

    - appends to table with above format and rows corresponding to the
      ideals of Q(sqrt(5)) with norm in v, and optionally creates a
      file

    EXAMPLES::

        sage: from sage.modular.hilbert.sqrt5_tables import dimensions
        sage: out = dimensions([1..40])
        4 1 2 ...
        5 1 -2*a+1 ...
        9 1 3 ...
        11 1 -3*a+1 ...
        11 1 -3*a+2 ...
        16 1 4 ...
        19 1 -4*a+1 ...
        19 1 -4*a+3 ...
        20 1 -4*a+2 ...
        25 1 5 ...
        29 1 a-6 ...
        29 1 -a-5 ...
        31 2 5*a-3 ...
        31 2 5*a-2 ...
        36 2 6 ...
        sage: out = dimensions([36, 4])
        36 2 6 ...
        4 1 2 ...

    Test writing to a file::

        sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt')
        sage: out = dimensions([36, 4], 'tmp_table.txt')
        36 2 6 ...
        4 1 2 ...
        sage: '36 2 6' in open('tmp_table.txt').read()
        True
        sage: open('tmp_table.txt').read().count('\n')
        2
        sage: os.unlink('tmp_table.txt')    
    """
    if len(v) == 0:
        return ""
    F = open(filename, "a") if filename else None
    out = ""
    for N in ideals_of_norm(v):
        t = cputime()
        H = IcosiansModP1ModN(N)
        tm = "%.2f" % cputime(t)
        s = "%s %s %s %s" % (N.norm(), H.cardinality(), no_space(reduced_gen(N)), tm)
        print s
        out += s + "\n"
        if F:
            F.write(s + "\n")
            F.flush()
    return out
def charpolys(v, B, filename=None):
    """
    Compute characteristic polynomials of T_P for primes P with norm
    <= B coprime to the level, for all spaces of Hilbert modular forms
    for all the levels in v.

    INPUT:

    - `v` -- list of positive integers
    - `B` -- positive integer
    - ``filename`` -- optional string; if given, output is also written
      to that file (in addition to stdout).

    OUTPUT:

    - outputs a table with rows corresponding to the ideals
      of Q(sqrt(5)) with norm in v, and optionally creates a file

    EXAMPLES::

        sage: from sage.modular.hilbert.sqrt5_tables import charpolys
        sage: out = charpolys([1..20], 10)
        4 2 ... [(5,x-6),(3,x-10)]
        5 -2*a+1 ... [(2,x-5),(3,x-10)]
        9 3 ... [(2,x-5),(5,x-6)]
        11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)]
        11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)]
        16 4 ... [(5,x-6),(3,x-10)]
        19 -4*a+1 ... [(2,x-5),(5,x-6),(3,x-10)]
        19 -4*a+3 ... [(2,x-5),(5,x-6),(3,x-10)]
        20 -4*a+2 ... [(3,x-10)]

        sage: out = charpolys([20, 11], 10)
        20 -4*a+2 ... [(3,x-10)]
        11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)]
        11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)]

    Test writing to a file::

        sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt')
        sage: out = charpolys([20, 11], 10, 'tmp_table.txt')
        20 -4*a+2 ... [(3,x-10)]
        11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)]
        11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)]
        sage: r = open('tmp_table.txt').read()
        sage: 'x-10' in r
        True
        sage: r.count('\n')
        3
        sage: os.unlink('tmp_table.txt')    
    """
    if len(v) == 0:
        return ""
    out = ""
    F = open(filename, "a") if filename else None
    P = [p for p in ideals_of_bounded_norm(B) if p.is_prime()]
    for N in ideals_of_norm(v):
        t = cputime()
        H = IcosiansModP1ModN(N)
        T = [
            (p.smallest_integer(), H.hecke_matrix(p).fcp()) for p in P if gcd(Integer(p.norm()), Integer(N.norm())) == 1
        ]
        tm = "%.2f" % cputime(t)
        s = "%s %s %s %s" % (N.norm(), no_space(reduced_gen(N)), tm, no_space(T))
        print s
        out += s + "\n"
        if F:
            F.write(s + "\n")
            F.flush()
    return out
def rational_newforms(v, B=100, filename=None, ncpu=1):
    """
    Return system of Hecke eigenvalues corresponding to rational
    newforms of level whose norm is in v.  Compute the Hecke
    eigenvalues a_P for all good primes P with norm < B.
    
    INPUT:

    - `v` -- list of integers
    - `B` -- positive integer
    - ``filename`` -- optional filename
    - ``ncpu`` -- positive integer (default: 1); if > 1 then use ncpu
      simultaneous processes.  Note that that displayed output during
      the computation and to the file may be out of order.  
    

    OUTPUT:

    - outputs a table with rows corresponding to the ideals
      of Q(sqrt(5)) with norm in v, and optionally creates a file

    Table columns:

        norm_of_level generator_of_level number time_for_level a_P a_P ... 

    EXAMPLES::

        sage: from sage.modular.hilbert.sqrt5_tables import rational_newforms
        sage: out = rational_newforms([1..76], B=20)
        31 5*a-3 0 ... -3 -2 2 4 -4 -4 4
        31 5*a-2 0 ... -3 -2 2 -4 4 4 -4
        36 6 0 ... ? -4 ? 2 2 0 0
        41 a-7 0 ... -2 -1 -4 -2 5 -1 6
        41 a+6 0 ... -2 -1 -4 5 -2 6 -1
        45 -6*a+3 0 ... -3 ? ? -4 -4 4 4
        49 7 0 ... 0 -4 5 -3 -3 0 0
        55 a+7 0 ... -1 ? -2 ? 0 8 -4
        55 -a+8 0 ... -1 ? -2 0 ? -4 8
        64 8 0 ... 0 -2 2 -4 -4 4 4
        71 a-9 0 ... -1 0 -2 0 0 -4 2
        71 a+8 0 ... -1 0 -2 0 0 2 -4
        76 -8*a+2 0 ... ? -3 1 3 -6 ? -7
        76 -8*a+2 1 ... ? 1 -5 -3 2 ? 5
        76 -8*a+6 0 ... ? -3 1 -6 3 -7 ?
        76 -8*a+6 1 ... ? 1 -5 2 -3 5 ?
        
    Test writing to a file::

        sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt')
        sage: out = rational_newforms([1..36], 20,'tmp_table.txt')
        31 5*a-3 0 ... -3 -2 2 4 -4 -4 4
        31 5*a-2 0 ... -3 -2 2 -4 4 4 -4
        36 6 0 ... ? -4 ? 2 2 0 0
        sage: r = open('tmp_table.txt').read()
        sage: r.count('\n')
        3
        sage: os.unlink('tmp_table.txt')    
    """
    if len(v) == 0:
        return ""

    if ncpu < 1:
        raise ValueError, "ncpu must be >= 1"

    F = open(filename, "a") if filename else None
    if ncpu > 1:
        from sage.all import parallel

        @parallel(ncpu)
        def f(N):
            return rational_newforms([N], B, filename=None, ncpu=1)

        d = {}
        for X in f(v):
            N = X[0][0]
            ans = X[1].strip()
            if ans:
                d[N] = ans
                if F:
                    F.write(ans + "\n")
        return "\n".join(d[N] for N in sorted(d.keys()))

    out = ""
    from sqrt5_hmf import QuaternionicModule

    for N in ideals_of_norm(v):
        t = cputime()
        H = QuaternionicModule(N)
        EC = H.rational_newforms()
        tm = "%.2f" % cputime(t)
        for i, E in enumerate(EC):
            v = E.aplist(B)
            data = [N.norm(), no_space(reduced_gen(N)), i, tm, " ".join([no_space(x) for x in v])]
            s = " ".join([str(x) for x in data])
            print s
            out += s + "\n"
            if F:
                F.write(s + "\n")
                F.flush()
    return out