Beispiel #1
0
 def test_random_prime_fields(self, num=16):
     n = 7
     k = 3
     for i in range(num):
         o = random_prime(2**128, lbound=n)
         s = randint(0, o - 1)
         assert s == templ_generic(7, 3, o, s)
Beispiel #2
0
    def _an_element_(self):
        r"""
        Return an element of the spectrum of the ring.

        OUTPUT:

        A point of the affine scheme ``self``.

        EXAMPLES::

            sage: Spec(QQ).an_element()
            Point on Spectrum of Rational Field defined by the Principal ideal (0) of Rational Field
            sage: Spec(ZZ).an_element()    # random output
            Point on Spectrum of Integer Ring defined by the Principal ideal (811) of Integer Ring
        """
        if self.coordinate_ring() is ZZ:
            from sage.rings.arith import random_prime
            return self(random_prime(1000))
        return self(0)
Beispiel #3
0
    def _an_element_(self):
        r"""
        Return an element of the spectrum of the ring.

        OUTPUT:

        A point of the affine scheme ``self``.

        EXAMPLES::

            sage: Spec(QQ).an_element()
            Point on Spectrum of Rational Field defined by the Principal ideal (0) of Rational Field
            sage: Spec(ZZ).an_element()    # random output
            Point on Spectrum of Integer Ring defined by the Principal ideal (811) of Integer Ring
        """
        if self.coordinate_ring() is ZZ:
            from sage.rings.arith import random_prime
            return self(ZZ.ideal(random_prime(1000)))
        return self(self.coordinate_ring().zero_ideal())
Beispiel #4
0
def random_blum_prime(lbound, ubound, ntries=100):
    r"""
    A random Blum prime within the specified bounds.

    Let `p` be a positive prime. Then `p` is a Blum prime if `p` is
    congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`.

    INPUT:

    - ``lbound`` -- positive integer; the lower bound on how small a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct from
      the upper bound.

    - ``ubound`` -- positive integer; the upper bound on how large a
      random Blum prime `p` can be. So we have
      ``0 < lbound <= p <= ubound``. The lower bound must be distinct
      from the upper bound.

    - ``ntries`` -- (default: ``100``) the number of attempts to generate
      a random Blum prime. If ``ntries`` is a positive integer, then
      perform that many attempts at generating a random Blum prime. This
      might or might not result in a Blum prime.

    OUTPUT:

    - A random Blum prime within the specified lower and upper bounds.

    .. NOTE::

        Beware that there might not be any primes between the lower and
        upper bounds. So make sure that these two bounds are
        "sufficiently" far apart from each other for there to be primes
        congruent to 3 modulo 4. In particular, there should be at least
        two distinct Blum primes within the specified bounds.

    EXAMPLES:

    Choose a random prime and check that it is a Blum prime::

        sage: from sage.crypto.util import random_blum_prime
        sage: p = random_blum_prime(10**4, 10**5)
        sage: is_prime(p)
        True
        sage: mod(p, 4) == 3
        True

    TESTS:

    Make sure that there is at least one Blum prime between the lower and
    upper bounds. In the following example, we have ``lbound=24`` and
    ``ubound=30`` with 29 being the only prime within those bounds. But 29
    is not a Blum prime. ::

        sage: from sage.crypto.util import random_blum_prime
        sage: random_blum_prime(24, 30, ntries=10)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
        sage: random_blum_prime(24, 28)
        Traceback (most recent call last):
        ...
        ValueError: No Blum primes within the specified closed interval.
    """
    # sanity check
    if not has_blum_prime(lbound, ubound):
        raise ValueError("No Blum primes within the specified closed interval.")
    # Now we know that there is a Blum prime within the closed interval
    # [lbound, ubound]. Pick one such prime at random.
    p = random_prime(ubound, lbound=lbound, proof=True)
    n = 1
    while mod(p, 4) != 3:
        p = random_prime(ubound, lbound=lbound, proof=True)
        n += 1
        if n > ntries:
            raise ValueError("Maximum number of attempts exceeded.")
    return p
def spanning_maass_products(ring,
                            weight,
                            subspaces=None,
                            lazy_rank_check=False):
    r"""This function is intended to quickly find good generators. This may be
    calculated in low precision and then ported to higher.
    
    the maass spaces of all subspaces are expected to be ordered the same way as
    ``ring.type()._maass_generator_preimages``.
    """
    ## Outline :
    ##  - get the basis as polynomials form the ring
    ##  - get the maass products, convert them to polynomials and try to
    ##  construct a basis
    ##  - get the maass space associated to this weight and get its coordinates
    ##  - return a basis containing of maass forms and the products

    if subspaces is None:
        subspaces = dict()

    assert ring.base_ring() is QQ or ring.base_ring() is ZZ, \
           "%s doesn't have rational base field" % ring

    dim = ring.graded_submodule(weight).dimension()
    precision = ring.fourier_expansion_precision()
    maass_lift_func = ring.type()._maass_generators
    maass_lift_preimage_func = ring.type()._maass_generator_preimages

    lift_polys = []
    preims = []
    monomials = set()

    for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 4), 3,
                    -2):
        if k not in subspaces:
            subspaces[k] = ring.graded_submodule(k)
        if weight - k not in subspaces:
            subspaces[weight - k] = ring.graded_submodule(weight - k)

        try:
            kgs = zip(subspaces[k].maass_space()._provided_basis(),
                      maass_lift_preimage_func(k))
            ogs = zip(subspaces[weight - k].maass_space()._provided_basis(),
                      maass_lift_preimage_func(weight - k))
        except ValueError:
            continue

        for f_coords, f_pre in kgs:
            for g_coords, g_pre in ogs:
                assert f_coords.parent().graded_ambient() is ring
                assert g_coords.parent().graded_ambient() is ring

                f = ring(f_coords)
                g = ring(g_coords)

                fg_poly = (f * g)._reduce_polynomial()
                monomials = monomials.union(set(fg_poly.monomials()))

                M = matrix(QQ,
                           len(lift_polys) + 1, [
                               poly.monomial_coefficient(m)
                               for poly in lift_polys + [fg_poly]
                               for m in monomials
                           ])

                # TODO : Use linbox
                try:
                    if magma(M).Rank() <= len(lift_polys):
                        continue
                except TypeError:
                    for i in xrange(10):
                        Mp = matrix(Qp(random_prime(10**10), 10), M)
                        if Mp.rank() > len(lift_polys): break
                    else:
                        if lazy_rank_check:
                            continue
                        elif M.rank() <= len(lift_polys):
                            continue

                lift_polys.append(fg_poly)
                preims.append([f_pre, g_pre])

                if len(lift_polys) == dim:
                    break

    if len(lift_polys) == dim:
        ss = construct_from_maass_products(ring, weight,
                                           zip(preims,
                                               lift_polys), True, False, True)
        poly_coords = [[0] * i + [1] + (len(lift_polys) - i - 1) * [0]
                       for i in xrange(len(lift_polys))]
    else:
        # The products of two Maass lifts don't span this space
        # we hence have to consider the Maass lifts
        ss = ring.graded_submodule(weight)
        poly_coords = [ss.coordinates(ring(p)) for p in lift_polys]

    maass_lifts = maass_lift_func(weight, precision)
    preims[0:0] = [[p] for p in maass_lift_preimage_func(weight)]
    all_coords = map(ss.coordinates, maass_lifts) + poly_coords

    M = matrix(QQ, all_coords).transpose()

    nmb_mls = len(maass_lifts)
    for i in xrange(10):
        Mp = matrix(Qp(random_prime(10**10), 10), M)
        pvs = Mp.pivots()
        if pvs[:nmb_mls] == range(nmb_mls) and \
           len(pvs) == dim :
            break
    else:
        pvs = M.pivots()

    if len(pvs) == dim:
        return [(preims[i], ring(ss(all_coords[i])).polynomial()) for i in pvs]
    else:
        raise RuntimeError, "The products of at most two Maass lifts don't span " + \
                            "this space"
def gritsenko_products(N, weight, dimension, precision = None) :
    """
    INPUT:
        - `N`           -- the level of the paramodular group.
        - ``weight``    -- the weight of the space, that we consider.
        - ``precision`` -- the precision to be used for the underlying
                           Fourier expansions.
    """
    min_precision = _minimal_paramodular_precision(N, weight)
    if precision is None :
        precision = min_precision
    else :
        precision = max(precision, min_precision)
    
    # - Lifts betrachten
    # - Zerlegung des Gewichts benutzen, dort Lifts nach und nach
    #   multiplizieren, um jeweils den Rang der unterliegenden Matrix
    #   zu bestimmen. Dabei mod p mit zufaelligem p arbeiten
        
    fourier_expansions = list(gritsenko_lift_subspace(N, weight, precision))
    products = [ [(weight, i)] for i in xrange(len(fourier_expansions)) ]

    fourier_indices = list(precision)
    fourier_matrix = matrix(ZZ, [ [e[k] for k in fourier_indices]
                                  for e in fourier_expansions] )
    
    for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 2), 1, -2) :
        space_k = list(enumerate(gritsenko_lift_subspace(N, k, precision)))
        space_wk = list(enumerate(gritsenko_lift_subspace(N, weight - k, precision)))

        if len(space_wk) == 0 :
            continue
        
        for (i,f) in space_k :
            
            for (j,g) in space_wk :
                if fourier_matrix.nrows() == dimension :
                    return products, fourier_expansions
                
                cur_fe = f * g
                # We are using lazy rank checks
                for p in [random_prime(10**10) for _ in range(2)] :
                    fourier_matrix_cur = matrix(GF(p), fourier_matrix.nrows() + 1,
                                                       fourier_matrix.ncols())
                    fourier_matrix_cur.set_block(0, 0, fourier_matrix)
                    fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0,
                                                  matrix(GF(p), 1, [cur_fe[fi] for fi in fourier_indices]) )
                    
                    if fourier_matrix_cur.rank() == fourier_matrix_cur.nrows() :
                        break
                else :
                    continue
                
                products.append( [(k, i), (weight - k, j)] )
                fourier_expansions.append(cur_fe)
                
                fourier_matrix_cur = matrix( ZZ, fourier_matrix.nrows() + 1,
                                                 fourier_matrix.ncols() )
                fourier_matrix_cur.set_block(0, 0, fourier_matrix)
                fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0,
                                              matrix(ZZ, 1, [cur_fe[fi] for fi in fourier_indices]) )
                fourier_matrix = fourier_matrix_cur
    
    return products, fourier_expansions
def construct_from_maass_products(ring,
                                  weight,
                                  products,
                                  is_basis=True,
                                  provides_maass_spezialschar=False,
                                  is_integral=False,
                                  lazy_rank_check=True):
    r"""
    Pass the return value of spanning_maass_products of a space of Siegel modular
    forms of same type. This will return a space using these forms.
    Whenever ``is_basis`` is False the the products are first filtered to yield a
    basis.
    """
    assert QQ.has_coerce_map_from(ring.base_ring()) or ring.base_ring() is ZZ, \
           "%s doesn't have rational base field" % ring

    dim = ring.graded_submodule(weight).dimension()

    ## if the products don't provide a basis, we have to choose one
    if not is_basis:
        ## we prefer to use Maass lifts, since they are very cheap
        maass_forms = []
        non_maass_forms = []
        for p in products:
            if len(p[0]) == 1:
                maass_forms.append(p)
            else:
                non_maass_forms.append(p)

        monomials = set()
        lift_polys = []
        products = []
        for lifts, lift_poly in maass_forms + non_maass_forms:
            red_poly = ring(
                ring.relations().ring()(lift_poly))._reduce_polynomial()
            monomials = monomials.union(set(red_poly.monomials()))

            M = matrix(QQ,
                       len(lift_polys) + 1, [
                           poly.monomial_coefficient(m)
                           for poly in lift_polys + [lift_poly]
                           for m in monomials
                       ])

            # TODO : Use linbox
            try:
                if magma(M).Rank() > len(lift_polys):
                    break
            except TypeError:
                for i in xrange(10):
                    Mp = matrix(Qp(random_prime(10**10), 10), M)
                    if Mp.rank() > len(lift_polys): break
                else:
                    if lazy_rank_check:
                        continue
                    elif M.rank() <= len(lift_polys):
                        continue

            lift_polys.append(red_poly)
            products.append((lifts, red_poly))

            if len(products) == dim:
                break
        else:
            raise ValueError, "products don't provide a basis"

    basis = []

    if provides_maass_spezialschar:
        maass_form_indices = []
    for i, (lifts, lift_poly) in enumerate(products):
        e = ring(ring.relations().ring()(lift_poly))

        if len(lifts) == 1:
            l = lifts[0]
            e._set_fourier_expansion(
                SiegelModularFormG2MaassLift(
                    l[0],
                    l[1],
                    ring.fourier_expansion_precision(),
                    is_integral=is_integral))
        elif len(lifts) == 2:
            (l0, l1) = tuple(lifts)
            e._set_fourier_expansion(
                EquivariantMonoidPowerSeries_LazyMultiplication(
                    SiegelModularFormG2MaassLift(
                        l0[0],
                        l0[1],
                        ring.fourier_expansion_precision(),
                        is_integral=is_integral),
                    SiegelModularFormG2MaassLift(
                        l1[0],
                        l1[1],
                        ring.fourier_expansion_precision(),
                        is_integral=is_integral)))

        else:
            e._set_fourier_expansion(
                prod(
                    SiegelModularFormG2MaassLift(
                        l[0], l[1], ring.precision(), is_integral=is_integral)
                    for l in lifts))
        basis.append(e)

        if provides_maass_spezialschar and len(lifts) == 1:
            maass_form_indices.append(i)

    ss = ring._submodule(basis,
                         grading_indices=(weight, ),
                         is_heckeinvariant=True)
    if provides_maass_spezialschar:
        maass_coords = [
            ss([0] * i + [1] + [0] * (dim - i - 1)) for i in maass_form_indices
        ]
        ss.maass_space.set_cache(
            SiegelModularFormG2Submodule_maassspace(ss, map(ss, maass_coords)))

    return ss
def spanning_maass_products(ring, weight, subspaces=None, lazy_rank_check=False):
    r"""This function is intended to quickly find good generators. This may be
    calculated in low precision and then ported to higher.
    
    the maass spaces of all subspaces are expected to be ordered the same way as
    ``ring.type()._maass_generator_preimages``.
    """
    ## Outline :
    ##  - get the basis as polynomials form the ring
    ##  - get the maass products, convert them to polynomials and try to
    ##  construct a basis
    ##  - get the maass space associated to this weight and get its coordinates
    ##  - return a basis containing of maass forms and the products

    if subspaces is None:
        subspaces = dict()

    assert ring.base_ring() is QQ or ring.base_ring() is ZZ, "%s doesn't have rational base field" % ring

    dim = ring.graded_submodule(weight).dimension()
    precision = ring.fourier_expansion_precision()
    maass_lift_func = ring.type()._maass_generators
    maass_lift_preimage_func = ring.type()._maass_generator_preimages

    lift_polys = []
    preims = []
    monomials = set()

    for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 4), 3, -2):
        if k not in subspaces:
            subspaces[k] = ring.graded_submodule(k)
        if weight - k not in subspaces:
            subspaces[weight - k] = ring.graded_submodule(weight - k)

        try:
            kgs = zip(subspaces[k].maass_space()._provided_basis(), maass_lift_preimage_func(k))
            ogs = zip(subspaces[weight - k].maass_space()._provided_basis(), maass_lift_preimage_func(weight - k))
        except ValueError:
            continue

        for f_coords, f_pre in kgs:
            for g_coords, g_pre in ogs:
                assert f_coords.parent().graded_ambient() is ring
                assert g_coords.parent().graded_ambient() is ring

                f = ring(f_coords)
                g = ring(g_coords)

                fg_poly = (f * g)._reduce_polynomial()
                monomials = monomials.union(set(fg_poly.monomials()))

                M = matrix(
                    QQ,
                    len(lift_polys) + 1,
                    [poly.monomial_coefficient(m) for poly in lift_polys + [fg_poly] for m in monomials],
                )

                # TODO : Use linbox
                try:
                    if magma(M).Rank() <= len(lift_polys):
                        continue
                except TypeError:
                    for i in xrange(10):
                        Mp = matrix(Qp(random_prime(10 ** 10), 10), M)
                        if Mp.rank() > len(lift_polys):
                            break
                    else:
                        if lazy_rank_check:
                            continue
                        elif M.rank() <= len(lift_polys):
                            continue

                lift_polys.append(fg_poly)
                preims.append([f_pre, g_pre])

                if len(lift_polys) == dim:
                    break

    if len(lift_polys) == dim:
        ss = construct_from_maass_products(ring, weight, zip(preims, lift_polys), True, False, True)
        poly_coords = [[0] * i + [1] + (len(lift_polys) - i - 1) * [0] for i in xrange(len(lift_polys))]
    else:
        # The products of two Maass lifts don't span this space
        # we hence have to consider the Maass lifts
        ss = ring.graded_submodule(weight)
        poly_coords = [ss.coordinates(ring(p)) for p in lift_polys]

    maass_lifts = maass_lift_func(weight, precision)
    preims[0:0] = [[p] for p in maass_lift_preimage_func(weight)]
    all_coords = map(ss.coordinates, maass_lifts) + poly_coords

    M = matrix(QQ, all_coords).transpose()

    nmb_mls = len(maass_lifts)
    for i in xrange(10):
        Mp = matrix(Qp(random_prime(10 ** 10), 10), M)
        pvs = Mp.pivots()
        if pvs[:nmb_mls] == range(nmb_mls) and len(pvs) == dim:
            break
    else:
        pvs = M.pivots()

    if len(pvs) == dim:
        return [(preims[i], ring(ss(all_coords[i])).polynomial()) for i in pvs]
    else:
        raise RuntimeError, "The products of at most two Maass lifts don't span " + "this space"
def construct_from_maass_products(
    ring, weight, products, is_basis=True, provides_maass_spezialschar=False, is_integral=False, lazy_rank_check=True
):
    r"""
    Pass the return value of spanning_maass_products of a space of Siegel modular
    forms of same type. This will return a space using these forms.
    Whenever ``is_basis`` is False the the products are first filtered to yield a
    basis.
    """
    assert QQ.has_coerce_map_from(ring.base_ring()) or ring.base_ring() is ZZ, (
        "%s doesn't have rational base field" % ring
    )

    dim = ring.graded_submodule(weight).dimension()

    ## if the products don't provide a basis, we have to choose one
    if not is_basis:
        ## we prefer to use Maass lifts, since they are very cheap
        maass_forms = []
        non_maass_forms = []
        for p in products:
            if len(p[0]) == 1:
                maass_forms.append(p)
            else:
                non_maass_forms.append(p)

        monomials = set()
        lift_polys = []
        products = []
        for lifts, lift_poly in maass_forms + non_maass_forms:
            red_poly = ring(ring.relations().ring()(lift_poly))._reduce_polynomial()
            monomials = monomials.union(set(red_poly.monomials()))

            M = matrix(
                QQ,
                len(lift_polys) + 1,
                [poly.monomial_coefficient(m) for poly in lift_polys + [lift_poly] for m in monomials],
            )

            # TODO : Use linbox
            try:
                if magma(M).Rank() > len(lift_polys):
                    break
            except TypeError:
                for i in xrange(10):
                    Mp = matrix(Qp(random_prime(10 ** 10), 10), M)
                    if Mp.rank() > len(lift_polys):
                        break
                else:
                    if lazy_rank_check:
                        continue
                    elif M.rank() <= len(lift_polys):
                        continue

            lift_polys.append(red_poly)
            products.append((lifts, red_poly))

            if len(products) == dim:
                break
        else:
            raise ValueError, "products don't provide a basis"

    basis = []

    if provides_maass_spezialschar:
        maass_form_indices = []
    for i, (lifts, lift_poly) in enumerate(products):
        e = ring(ring.relations().ring()(lift_poly))

        if len(lifts) == 1:
            l = lifts[0]
            e._set_fourier_expansion(
                SiegelModularFormG2MaassLift(l[0], l[1], ring.fourier_expansion_precision(), is_integral=is_integral)
            )
        elif len(lifts) == 2:
            (l0, l1) = tuple(lifts)
            e._set_fourier_expansion(
                EquivariantMonoidPowerSeries_LazyMultiplication(
                    SiegelModularFormG2MaassLift(
                        l0[0], l0[1], ring.fourier_expansion_precision(), is_integral=is_integral
                    ),
                    SiegelModularFormG2MaassLift(
                        l1[0], l1[1], ring.fourier_expansion_precision(), is_integral=is_integral
                    ),
                )
            )

        else:
            e._set_fourier_expansion(
                prod(SiegelModularFormG2MaassLift(l[0], l[1], ring.precision(), is_integral=is_integral) for l in lifts)
            )
        basis.append(e)

        if provides_maass_spezialschar and len(lifts) == 1:
            maass_form_indices.append(i)

    ss = ring._submodule(basis, grading_indices=(weight,), is_heckeinvariant=True)
    if provides_maass_spezialschar:
        maass_coords = [ss([0] * i + [1] + [0] * (dim - i - 1)) for i in maass_form_indices]
        ss.maass_space.set_cache(SiegelModularFormG2Submodule_maassspace(ss, map(ss, maass_coords)))

    return ss
def test_correctness_and_precision_of_solve_diff_eqn(number=20, verbosity=1):
    """
    ``number`` is how many different random distributions to check. 
    
    Currently, avoids the prime 2.
    """
    from sage.misc.prandom import randint
    from sage.rings.arith import random_prime
    from sage.rings.padics.factory import ZpCA
    from sage.modular.pollack_stevens.coeffmod_OMS_space import OverconvergentDistributions
    from sage.structure.sage_object import dumps
    errors = []
    munus = []
    for i in range(number):
        Mspace = randint(1, 20)    #Moments of space
        M = randint(max(0, Mspace - 5), Mspace)
        p = random_prime(13, lbound=3)
        k = randint(0, 6)
        Rprec = Mspace + randint(0, 5)
        R = ZpCA(p, Rprec)
        D = OverconvergentDistributions(k, base=R, prec_cap=Mspace)
        S0 = D.action().actor()
        Delta_mat = S0([1,1,0,1])
        mu = D.random_element(M)
        mu_save = dumps(mu)#[deepcopy(mu.ordp), deepcopy(mu._moments)]
        if verbosity > 0:
            print "\nTest #{0} data (Mspace, M, p, k, Rprec) =".format(i+1), (Mspace, M, p, k, Rprec)
            print "mu =", mu
        
        nu = mu * Delta_mat - mu
        nu_save = [deepcopy(nu.ordp), deepcopy(nu._moments)]
        mu2 = nu.solve_diff_eqn()
        nu_abs_prec = nu.precision_absolute()
        expected = nu_abs_prec - nu_abs_prec.exact_log(p) - 1
        if M != 1:
            try:
                agree = (mu - mu2).is_zero(expected)
            except PrecisionError:
                print (Mspace, M, p, k, Rprec), mu_save._repr_(), nu_save
                assert False
        else:
            agree = mu2.is_zero(expected)
        if verbosity > 1:
            print "    Just so you know:"
            print "     mured =", mu.reduce_precision_absolute(expected)
            print "       mu2 =", mu2
            print "        nu = ", nu
        if not agree:
            errors.append((i+1, 1))
            munus.append((mu_save, nu_save, mu2, (Mspace, M, p, k, Rprec)))
        if verbosity > 0:
            print "    Test finding mu from mu|Delta accurate: %s"%(agree)
            print "        nu_abs_prec  soln_abs_prec_expected  actual  agree"
        mu2_abs_prec = mu2.precision_absolute()
        agree = (expected == mu2_abs_prec)
        if verbosity > 0:
            print "        %s             %s                       %s      %s"%(nu_abs_prec, expected, mu2_abs_prec, agree)
        if not agree:
            errors.append((i+1, 2))
            munus.append((mu_save, nu_save, mu2, (Mspace, M, p, k, Rprec)))
        
        if mu.precision_relative() > 0:
            mu._moments[0] = R(0, mu.precision_relative())
        mu_save = [deepcopy(mu.ordp), deepcopy(mu._moments)]
        if verbosity > 0:
            print "    mu modified =", mu
        nu = mu.solve_diff_eqn()
        mu_abs_prec = mu.precision_absolute()
        expected = mu_abs_prec - mu_abs_prec.exact_log(p) - 1
        nud = nu * Delta_mat - nu
        nu_save = [deepcopy(nu.ordp), deepcopy(nu._moments)]
        agree = (nud - mu).is_zero(expected)
        if verbosity > 1:
            print "    Just so you know:"
            print "        mu =", mu
            print "     mured =", mu.reduce_precision_absolute(expected)
            print "       nud =", nud
        if not agree:
            errors.append((i+1, 3))
            munus.append((mu_save, nu_save, (Mspace, M, p, k, Rprec)))
        if verbosity > 0:
            print "    Test finding nu with nu|Delta == mu: %s"%(agree)
            print "        mu_abs_prec  soln_abs_prec_expected  actual  agree"
        nu_abs_prec = nu.precision_absolute()
        agree = (expected == nu_abs_prec)
        if verbosity > 0:
            print "        %s             %s                       %s      %s"%(mu_abs_prec, expected, nu_abs_prec, agree)
        if not agree:
            errors.append((i+1, 4))
            munus.append((mu_save, nu_save, (Mspace, M, p, k, Rprec)))
    if len(errors) == 0:
        if verbosity > 0:
            print "\nTest passed with no errors."
        return
    if verbosity > 0:
        print "\nTest failed with errors: %s\n"%(errors)
    return errors, munus
def gritsenko_products(N, weight, dimension, precision=None):
    """
    INPUT:
        - `N`           -- the level of the paramodular group.
        - ``weight``    -- the weight of the space, that we consider.
        - ``precision`` -- the precision to be used for the underlying
                           Fourier expansions.
    """
    min_precision = _minimal_paramodular_precision(N, weight)
    if precision is None:
        precision = min_precision
    else:
        precision = max(precision, min_precision)

    # - Lifts betrachten
    # - Zerlegung des Gewichts benutzen, dort Lifts nach und nach
    #   multiplizieren, um jeweils den Rang der unterliegenden Matrix
    #   zu bestimmen. Dabei mod p mit zufaelligem p arbeiten

    fourier_expansions = list(gritsenko_lift_subspace(N, weight, precision))
    products = [[(weight, i)] for i in xrange(len(fourier_expansions))]

    fourier_indices = list(precision)
    fourier_matrix = matrix(ZZ, [[e[k] for k in fourier_indices]
                                 for e in fourier_expansions])

    for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 2), 1,
                    -2):
        space_k = list(enumerate(gritsenko_lift_subspace(N, k, precision)))
        space_wk = list(
            enumerate(gritsenko_lift_subspace(N, weight - k, precision)))

        if len(space_wk) == 0:
            continue

        for (i, f) in space_k:

            for (j, g) in space_wk:
                if fourier_matrix.nrows() == dimension:
                    return products, fourier_expansions

                cur_fe = f * g
                # We are using lazy rank checks
                for p in [random_prime(10**10) for _ in range(2)]:
                    fourier_matrix_cur = matrix(GF(p),
                                                fourier_matrix.nrows() + 1,
                                                fourier_matrix.ncols())
                    fourier_matrix_cur.set_block(0, 0, fourier_matrix)
                    fourier_matrix_cur.set_block(
                        fourier_matrix.nrows(), 0,
                        matrix(GF(p), 1,
                               [cur_fe[fi] for fi in fourier_indices]))

                    if fourier_matrix_cur.rank() == fourier_matrix_cur.nrows():
                        break
                else:
                    continue

                products.append([(k, i), (weight - k, j)])
                fourier_expansions.append(cur_fe)

                fourier_matrix_cur = matrix(ZZ,
                                            fourier_matrix.nrows() + 1,
                                            fourier_matrix.ncols())
                fourier_matrix_cur.set_block(0, 0, fourier_matrix)
                fourier_matrix_cur.set_block(
                    fourier_matrix.nrows(), 0,
                    matrix(ZZ, 1, [cur_fe[fi] for fi in fourier_indices]))
                fourier_matrix = fourier_matrix_cur

    return products, fourier_expansions