def iter_positive_forms_with_content(self):
        if self.__disc is infinity:
            raise ValueError, "infinity is not a true filter index"

        if self.__reduced:
            for a in xrange(1, isqrt(self.__disc // 3) + 1):
                for b in xrange(a + 1):
                    g = gcd(a, b)
                    for c in xrange(a,
                                    (b**2 + (self.__disc - 1)) // (4 * a) + 1):
                        yield (a, b, c), gcd(g, c)
        else:
            maxtrace = floor(5 * self.__disc / 15 + sqrt(self.__disc) / 2)
            for a in xrange(1, maxtrace + 1):
                for c in xrange(1, maxtrace - a + 1):
                    g = gcd(a, c)

                    Bu = isqrt(4 * a * c - 1)

                    di = 4 * a * c - self.__disc
                    if di >= 0:
                        Bl = isqrt(di) + 1
                    else:
                        Bl = 0

                    for b in xrange(-Bu, -Bl + 1):
                        yield (a, b, c), gcd(g, b)
                    for b in xrange(Bl, Bu + 1):
                        yield (a, b, c), gcd(g, b)
        #! if self.__reduced

        raise StopIteration
    def iter_positive_forms_with_content(self) :
        if self.__disc is infinity :
            raise ValueError, "infinity is not a true filter index"
        
        
        if self.__reduced :        
            for a in xrange(1,isqrt(self.__disc // 3) + 1) :
                for b in xrange(a+1) :
                    g = gcd(a, b)
                    for c in xrange(a, (b**2 + (self.__disc - 1))//(4*a) + 1) :
                        yield (a,b,c), gcd(g,c)
        else :
            maxtrace = floor(5*self.__disc / 15 + sqrt(self.__disc)/2)
            for a in xrange(1, maxtrace + 1) :
                for c in xrange(1, maxtrace - a + 1) :
                    g = gcd(a,c)
                    
                    Bu = isqrt(4*a*c - 1)

                    di = 4*a*c - self.__disc
                    if di >= 0 :
                        Bl = isqrt(di) + 1 
                    else :
                        Bl = 0
                    
                    for b in xrange(-Bu, -Bl + 1) :
                        yield (a,b,c), gcd(g,b)
                    for b in xrange(Bl, Bu + 1) :
                        yield (a,b,c), gcd(g,b)
        #! if self.__reduced

        raise StopIteration
Example #3
0
 def order_at_cusp(self, cusp):
     r"""
     Return the order of vanishing of self at the given cusp.
     
     INPUT:
     
     
     -  ``cusp`` -  a CuspFamily object
     
     
     OUTPUT:
     
     
     - an integer
     
     
     EXAMPLES::
     
         sage: e = EtaProduct(2, {2:24, 1:-24})
         sage: e.order_at_cusp(CuspFamily(2, 1)) # cusp at infinity
         1
         sage: e.order_at_cusp(CuspFamily(2, 2)) # cusp 0
         -1
     """
     if not isinstance(cusp, CuspFamily):
         raise TypeError, "Argument (=%s) should be a CuspFamily" % cusp
     if cusp.level() != self.level():
         raise ValueError, "Cusp not on right curve!"
     return 1 / ZZ(24) / gcd(cusp.width(),
                             self.level() // cusp.width()) * sum([
                                 ell * self.r(ell) / cusp.width() *
                                 (gcd(cusp.width(),
                                      self.level() // ell))**2
                                 for ell in self._keys
                             ])
Example #4
0
def circle_image(A, B):
    G = Graphics()
    G += circle((0, 0), 1, color='grey')
    from collections import defaultdict
    tmp = defaultdict(int)
    for a in A:
        for j in range(a):
            if gcd(j, a) == 1:
                rational = Rational(j) / Rational(a)
                tmp[(rational.numerator(), rational.denominator())] += 1

    for b in B:
        for j in range(b):
            if gcd(j, b) == 1:
                rational = Rational(j) / Rational(b)
                tmp[(rational.numerator(), rational.denominator())] -= 1
    C = ComplexField()
    for val in tmp:
        if tmp[val] > 0:
            G += text(str(tmp[val]),
                      exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color="green")
        if tmp[val] < 0:
            G += text(str(abs(tmp[val])),
                      exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color="blue")
    return G
Example #5
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbf{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Lemma 3.2 in Cremona's 1997 book shows that for the action
            of Gamma1(N) on "signed projective space"
            `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`
            if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod
            gcd(v_1, N)`. It follows that every orbit has a
            representative `u/v` with `v \le N` and `0 \le u \le
            gcd(v, N)`.  We iterate through all pairs `(u,v)`
            satisfying this.

            Having found a set containing at least one of every
            equivalence class modulo Gamma1(N), we can be sure of
            picking up every class modulo GammaH(N) since this
            contains Gamma1(N); and the reduce_cusp call does the
            checking to make sure we don't get any duplicates.

        EXAMPLES::

            sage: Gamma1(5)._find_cusps()
            [0, 2/5, 1/2, Infinity]
            sage: Gamma1(35)._find_cusps()
            [0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]
            sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')
            True
            sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')
            True
        """

        s = []
        hashes = []
        N = self.level()

        for d in xrange(1, 1 + N):
            w = N.gcd(d)
            M = int(w) if w > 1 else 2
            for a in xrange(1, M):
                if gcd(a, w) != 1:
                    continue
                while gcd(a, d) != 1:
                    a += w
                c = self.reduce_cusp(Cusp(a, d))
                h = hash(c)
                if not h in hashes:
                    hashes.append(h)
                    s.append(c)
        return sorted(s)
Example #6
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbf{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Lemma 3.2 in Cremona's 1997 book shows that for the action
            of Gamma1(N) on "signed projective space"
            `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2`
            if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod
            gcd(v_1, N)`. It follows that every orbit has a
            representative `u/v` with `v \le N` and `0 \le u \le
            gcd(v, N)`.  We iterate through all pairs `(u,v)`
            satisfying this.

            Having found a set containing at least one of every
            equivalence class modulo Gamma1(N), we can be sure of
            picking up every class modulo GammaH(N) since this
            contains Gamma1(N); and the reduce_cusp call does the
            checking to make sure we don't get any duplicates.

        EXAMPLES::

            sage: Gamma1(5)._find_cusps()
            [0, 2/5, 1/2, Infinity]
            sage: Gamma1(35)._find_cusps()
            [0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity]
            sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym')
            True
            sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym')
            True
        """

        s = []
        hashes = []
        N = self.level()

        for d in xrange(1, 1+N):
            w = N.gcd(d)
            M = int(w) if w > 1 else 2
            for a in xrange(1,M):
                if gcd(a, w) != 1:
                    continue
                while gcd(a, d) != 1:
                    a += w
                c = self.reduce_cusp(Cusp(a,d))
                h = hash(c)
                if not h in hashes:
                    hashes.append(h)
                    s.append(c)
        return sorted(s)
def _goodness(n, R, p):
    """
    Return the goodness of ``n`` for the sequence ``R`` and the prime ``p`` -- that is the largest
    non-``p`` prime power dividing ``period(n)``.

    INPUT:

    - ``n`` --  an integer

    - ``R`` -- an object in the class ``BinaryRecurrenceSequence``

    - ``p`` -- a rational prime

    OUTPUT:

    - An integer which is the "goodness" of ``n``, i.e. the largest non-``p`` prime power dividing ``period(n)``.

    EXAMPLES::

        sage: R = BinaryRecurrenceSequence(11,2)
        sage: sage.combinat.binary_recurrence_sequences._goodness(89,R,7)
        11

        sage: R = BinaryRecurrenceSequence(1,1)
        sage: sage.combinat.binary_recurrence_sequences._goodness(13,R,7)
        4
        sage: R.period(13)        #the period of R mod 13 is divisible by 7
        28

    """

    #The period of R mod ell
    K = R.period(n)

    return _largest_ppower_divisor(K / gcd(K, p))
    def _test__jacobi_predicted_taylor_coefficients(fs, q_precision) :
        r"""
        Given a list of power series, which are the corrected Taylor coefficients
        of a Jacobi form, return the renormalized uncorrected ones, assuming that
        all but one `f` vanish.
        
        INPUT:
        
        - ``fs`` -- A list of power series.
        
        - ``q_precision`` -- An integer.
        
        OUPUT:
        
        - A list of power series.
        
        TESTS:
        
        See jacobi_form_by_taylor_expansion.
        """
        from sage.rings.arith import gcd
        
        R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0)
        
        diff = lambda f: f.derivative().shift(1)
        normalize = lambda f: f / gcd(f.list()) if f != 0 else f
        diffnorm = lambda f,l: normalize(reduce(lambda a, g: g(a), l*[diff], f))

        taylor_coefficients = list()
        allf = R(0)
        for f in fs :
            allf = f(q_precision) + diffnorm(allf, 1)            
            taylor_coefficients.append(allf)

        return taylor_coefficients
Example #9
0
    def random_element(self, bound=100, *args, **kwds):
        r"""
        Return a random element of `{\rm SL}_2(\ZZ)` with entries whose
        absolute value is strictly less than bound (default 100).
        Additional arguments and keywords are passed to the random_element
        method of ZZ.

        (Algorithm: Generate a random pair of integers at most bound. If they
        are not coprime, throw them away and start again. If they are, find an
        element of `{\rm SL}_2(\ZZ)` whose bottom row is that, and
        left-multiply it by `\begin{pmatrix} 1 & w \\ 0 & 1\end{pmatrix}` for
        an integer `w` randomly chosen from a small enough range that the
        answer still has entries at most bound.)

        It is, unfortunately, not true that all elements of SL2Z with entries <
        bound appear with equal probability; those with larger bottom rows are
        favoured, because there are fewer valid possibilities for w.

        EXAMPLES::

            sage: SL2Z.random_element()
            [60 13]
            [83 18]
            sage: SL2Z.random_element(5)
            [-1  3]
            [ 1 -4]

        Passes extra positional or keyword arguments through::

            sage: SL2Z.random_element(5, distribution='1/n')
            [ 1 -4]
            [ 0  1]
        """
        if bound <= 1: raise ValueError("bound must be greater than 1")
        c = ZZ.random_element(1-bound, bound, *args, **kwds)
        d = ZZ.random_element(1-bound, bound, *args, **kwds)
        if gcd(c,d) != 1: # try again
            return self.random_element(bound, *args, **kwds)
        else:
            a,b,c,d = lift_to_sl2z(c,d,0)
            whi = bound
            wlo = bound
            if c > 0:
                whi = min(whi, ((bound - a)/ZZ(c)).ceil())
                wlo = min(wlo, ((bound + a)/ZZ(c)).ceil())
            elif c < 0:
                whi = min(whi, ((bound + a)/ZZ(-c)).ceil())
                wlo = min(wlo, ((bound - a)/ZZ(-c)).ceil())

            if d > 0:
                whi = min(whi, ((bound - b)/ZZ(d)).ceil())
                wlo = min(wlo, ((bound + b)/ZZ(d)).ceil())
            elif d < 0:
                whi = min(whi, ((bound + b)/ZZ(-d)).ceil())
                wlo = min(wlo, ((bound - b)/ZZ(-d)).ceil())

            w = ZZ.random_element(1-wlo, whi, *args, **kwds)
            a += c*w
            b += d*w
            return self([a,b,c,d])
 def eval(self, expansion, weight = None) :
     if weight is None :
         try :
             weight = expansion.weight()
         except AttributeError :
             raise ValueError, "weight must be defined for the Hecke action"
     
     precision = expansion.precision()
     if precision.is_infinite() :
         precision = expansion._bounding_precision()
     else :
         precision = precision._hecke_operator(self.__l)
     characters = expansion.non_zero_components()
     expansion_level = precision.level()
     
     if gcd(expansion_level, self.__l) != 1 :
         raise ValueError, "Level of expansion and of Hecke operator must be coprime."
     
     hecke_expansion = dict()
     for ch in characters :
         hecke_expansion[ch] = dict( (k, self.hecke_coeff(expansion, ch, k, weight, expansion_level))
                                     for k in precision )
     
     result = expansion.parent()._element_constructor_(hecke_expansion)
     result._set_precision(expansion.precision()._hecke_operator(self.__l))
     
     return result
Example #11
0
    def random_element(self, bound=100, *args, **kwds):
        r"""
        Return a random element of `{\rm SL}_2(\ZZ)` with entries whose
        absolute value is strictly less than bound (default 100).
        Additional arguments and keywords are passed to the random_element
        method of ZZ.

        (Algorithm: Generate a random pair of integers at most bound. If they
        are not coprime, throw them away and start again. If they are, find an
        element of `{\rm SL}_2(\ZZ)` whose bottom row is that, and
        left-multiply it by `\begin{pmatrix} 1 & w \\ 0 & 1\end{pmatrix}` for
        an integer `w` randomly chosen from a small enough range that the
        answer still has entries at most bound.)

        It is, unfortunately, not true that all elements of SL2Z with entries <
        bound appear with equal probability; those with larger bottom rows are
        favoured, because there are fewer valid possibilities for w.

        EXAMPLES::

            sage: SL2Z.random_element()
            [60 13]
            [83 18]
            sage: SL2Z.random_element(5)
            [-1  3]
            [ 1 -4]

        Passes extra positional or keyword arguments through::

            sage: SL2Z.random_element(5, distribution='1/n')
            [ 1 -4]
            [ 0  1]
        """
        if bound <= 1: raise ValueError("bound must be greater than 1")
        c = ZZ.random_element(1 - bound, bound, *args, **kwds)
        d = ZZ.random_element(1 - bound, bound, *args, **kwds)
        if gcd(c, d) != 1:  # try again
            return self.random_element(bound, *args, **kwds)
        else:
            a, b, c, d = lift_to_sl2z(c, d, 0)
            whi = bound
            wlo = bound
            if c > 0:
                whi = min(whi, ((bound - a) / ZZ(c)).ceil())
                wlo = min(wlo, ((bound + a) / ZZ(c)).ceil())
            elif c < 0:
                whi = min(whi, ((bound + a) / ZZ(-c)).ceil())
                wlo = min(wlo, ((bound - a) / ZZ(-c)).ceil())

            if d > 0:
                whi = min(whi, ((bound - b) / ZZ(d)).ceil())
                wlo = min(wlo, ((bound + b) / ZZ(d)).ceil())
            elif d < 0:
                whi = min(whi, ((bound + b) / ZZ(-d)).ceil())
                wlo = min(wlo, ((bound - b) / ZZ(-d)).ceil())

            w = ZZ.random_element(1 - wlo, whi, *args, **kwds)
            a += c * w
            b += d * w
            return self([a, b, c, d])
Example #12
0
def num_cusps_of_width(N, d):
    r"""
    Return the number of cusps on `X_0(N)` of width d.

    INPUT:


    -  ``N`` - (integer): the level

    -  ``d`` - (integer): an integer dividing N, the cusp
       width


    EXAMPLES::

        sage: [num_cusps_of_width(18,d) for d in divisors(18)]
        [1, 1, 2, 2, 1, 1]
    """
    try:
        N = ZZ(N)
        d = ZZ(d)
        assert N>0
        assert d>0
        assert ((N % d) == 0)
    except TypeError:
        raise TypeError, "N and d must be integers"
    except AssertionError:
        raise AssertionError, "N and d must be positive integers with d|N"

    return euler_phi(gcd(d, N//d))
    def get_representatives( self, t, N) :
        r"""
        A helper function used in hecke_coeff that computes the right
        coset representatives of $\Gamma^0(t) \cap \Gamma_0(N) \ Gamma_0(N)$ where
        $\Gamma^0(t)$ is the subgroup of $SL(2,Z)$ where the upper right hand
        corner is divisible by $t$.

        NOTE
            We use the bijection $\Gamma^0(t)\SL(2,Z) \rightarrow P^1(\Z/t\Z)$
            given by $A \mapsto [1:0]A$.
        """
        if t == 1 : return [(1,0,0,1)]
        
        rep_list = []
        
        for (x,y) in P1List(t):
            ## we know that (N, x, y) = 1
            N1 = gcd(N, x)
            if N1 != 1 :
                x = x + t * N // N1

            ## we calculate a pair c,d satisfying a minimality condition
            ## to make later multiplications cheaper
            (_, d, c) = Integer(x)._xgcd(Integer(N * y), minimal=True)
            
            #print (x, y, -N * c, d)
            rep_list.append((x, y, -N * c, d))
                
        return rep_list
Example #14
0
def num_cusps_of_width(N, d):
    r""" 
    Return the number of cusps on `X_0(N)` of width d.
    
    INPUT:
    
    
    -  ``N`` - (integer): the level
    
    -  ``d`` - (integer): an integer dividing N, the cusp
       width
    
    
    EXAMPLES::
    
        sage: [num_cusps_of_width(18,d) for d in divisors(18)]
        [1, 1, 2, 2, 1, 1]
    """
    try:
        N = ZZ(N)
        d = ZZ(d)
        assert N > 0
        assert d > 0
        assert ((N % d) == 0)
    except TypeError:
        raise TypeError, "N and d must be integers"
    except AssertionError:
        raise AssertionError, "N and d must be positive integers with d|N"

    return euler_phi(gcd(d, N // d))
Example #15
0
def _normalize_H(H, level):
    """
    Normalize representatives for a given subgroup H of the units
    modulo level.

    NOTE: This function does *not* make any attempt to find a minimal
    set of generators for H. It simply normalizes the inputs for use
    in hashing.

    EXAMPLES::

        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)
        [3]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)
        [5]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)
        Traceback (most recent call last):
        ...
        ArithmeticError: The generators [4, 18] must be units modulo 14
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)
        [3]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)
        [7, 9]
    """
    H = [ZZ(h) for h in H]
    for h in H:
        if gcd(h, level) > 1:
            raise ArithmeticError, 'The generators %s must be units modulo %s' % (
                H, level)
    H = list(set([h % level for h in H]))
    H.sort()
    if 1 in H:
        H.remove(1)
    return H
Example #16
0
def test_Hecke_relations(a,b,C):
    r"""Testing Hecke relations for the Fourier coefficients in C



    INPUT:
    -''C'' -- dictionary of complex (Fourier coefficients)
    -''a'' -- integer
    -''b'' -- integer

    OUTPUT:
    -''diff'' -- real : |C(a)C(b)-C(ab)| if (a,b)=1

    EXAMPLE::

    
    sage: S=MaassWaveForms(Gamma0(1))
    sage: R=mpmath.mpf(9.53369526135355755434423523592877032382125639510725198237579046413534)
    sage: Y=mpmath.mpf(0.85)
    sage: C=coefficients_for_Maass_waveforms(S,R,Y,10,20,12)
    sage: d=test_Hecke_relations(C,2,3); mppr(d)
    '9.29e-8'
    sage: C=coefficients_for_Maass_waveforms(S,R,Y,30,50,20)
    sage: d=test_Hecke_relations(C,2,3); mppr(d)
    '3.83e-43'
    
    
    """
    c=gcd(Integer(a),Integer(b))
    lhs=C[0][a]*C[0][b]
    rhs=mpmath.mpf(0)
    for d in divisors(c):
        rhs=rhs+C[0][Integer(a*b/d/d)]
    return abs(rhs-lhs)
Example #17
0
    def get_representatives(self, t, N):
        r"""
        A helper function used in hecke_coeff that computes the right
        coset representatives of $\Gamma^0(t) \cap \Gamma_0(N) \ Gamma_0(N)$ where
        $\Gamma^0(t)$ is the subgroup of $SL(2,Z)$ where the upper right hand
        corner is divisible by $t$.

        NOTE
            We use the bijection $\Gamma^0(t)\SL(2,Z) \rightarrow P^1(\Z/t\Z)$
            given by $A \mapsto [1:0]A$.
        """
        if t == 1: return [(1, 0, 0, 1)]

        rep_list = []

        for (x, y) in P1List(t):
            ## we know that (N, x, y) = 1
            N1 = gcd(N, x)
            if N1 != 1:
                x = x + t * N // N1

            ## we calculate a pair c,d satisfying a minimality condition
            ## to make later multiplications cheaper
            (_, d, c) = Integer(x)._xgcd(Integer(N * y), minimal=True)

            #print (x, y, -N * c, d)
            rep_list.append((x, y, -N * c, d))

        return rep_list
Example #18
0
def _list_subgroup(N, gens):
    r"""
    Given an integer ``N`` and a list of integers ``gens``, return a list of
    the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the
    elements of ``gens``.

    EXAMPLE::

        sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])
        [1, 3, 4, 5, 9]
    """
    H = set([1])
    N = int(N)
    for g in gens:
        if gcd(g, N) != 1:
            raise ValueError, "gen (=%s) is not in (Z/%sZ)^*" % (g, N)
        gk = int(g) % N
        sbgrp = [gk]
        while not (gk in H):
            gk = (gk * g) % N
            sbgrp.append(gk)
        H = set([(x * h) % N for x in sbgrp for h in H])
    H = list(H)
    H.sort()
    return H
Example #19
0
def _normalize_H(H, level):
    """
    Normalize representatives for a given subgroup H of the units
    modulo level.

    NOTE: This function does *not* make any attempt to find a minimal
    set of generators for H. It simply normalizes the inputs for use
    in hashing.

    EXAMPLES::

        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10)
        [3]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7)
        [5]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14)
        Traceback (most recent call last):
        ...
        ArithmeticError: The generators [4, 18] must be units modulo 14
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14)
        [3]
        sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10)
        [7, 9]
    """
    H = [ZZ(h) for h in H]
    for h in H:
        if gcd(h, level) > 1:
            raise ArithmeticError, 'The generators %s must be units modulo %s'%(H, level)
    H = list(set([h%level for h in H]))
    H.sort()
    if 1 in H:
        H.remove(1)
    return H
Example #20
0
    def cardinality(self):
        """
        Returns the number of integer necklaces with the evaluation e.

        EXAMPLES::

            sage: Necklaces([]).cardinality()
            0
            sage: Necklaces([2,2]).cardinality()
            2
            sage: Necklaces([2,3,2]).cardinality()
            30

        Check to make sure that the count matches up with the number of
        Lyndon words generated.

        ::

            sage: comps = [[],[2,2],[3,2,7],[4,2]]+Compositions(4).list()
            sage: ns = [ Necklaces(comp) for comp in comps]
            sage: all( [ n.cardinality() == len(n.list()) for n in ns] )
            True
        """
        evaluation = self.e
        le = list(evaluation)
        if len(le) == 0:
            return 0

        n = sum(le)

        return sum([
            euler_phi(j) * factorial(n / j) /
            prod([factorial(ni / j) for ni in evaluation])
            for j in divisors(gcd(le))
        ]) / n
Example #21
0
    def order_at_cusp(self, cusp):
        r"""
        Return the order of vanishing of self at the given cusp.

        INPUT:


        -  ``cusp`` -  a CuspFamily object


        OUTPUT:


        - an integer


        EXAMPLES::

            sage: e = EtaProduct(2, {2:24, 1:-24})
            sage: e.order_at_cusp(CuspFamily(2, 1)) # cusp at infinity
            1
            sage: e.order_at_cusp(CuspFamily(2, 2)) # cusp 0
            -1
        """
        if not isinstance(cusp, CuspFamily):
            raise TypeError("Argument (=%s) should be a CuspFamily" % cusp)
        if cusp.level() != self.level():
            raise ValueError("Cusp not on right curve!")
        return 1/ZZ(24)/gcd(cusp.width(), self.level()//cusp.width()) * sum( [ell*self.r(ell)/cusp.width() * (gcd(cusp.width(), self.level()//ell))**2  for ell in self._keys] )
Example #22
0
def _list_subgroup(N, gens):
    r"""
    Given an integer ``N`` and a list of integers ``gens``, return a list of
    the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the
    elements of ``gens``.

    EXAMPLE::

        sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3])
        [1, 3, 4, 5, 9]
    """
    H = set([1])
    N = int(N)
    for g in gens:
        if gcd(g, N) != 1:
            raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N)
        gk = int(g) % N
        sbgrp = [gk]
        while not (gk in H):
            gk = (gk * g)%N
            sbgrp.append(gk)
        H = set([(x*h)%N for x in sbgrp for h in H])
    H = list(H)
    H.sort()
    return H
Example #23
0
def num_cusps_of_width(N, d):
    r"""
    Return the number of cusps on `X_0(N)` of width d.

    INPUT:


    -  ``N`` - (integer): the level

    -  ``d`` - (integer): an integer dividing N, the cusp
       width


    EXAMPLES::

        sage: [num_cusps_of_width(18,d) for d in divisors(18)]
        [1, 1, 2, 2, 1, 1]
        sage: num_cusps_of_width(4,8)
        Traceback (most recent call last):
        ...
        ValueError: N and d must be positive integers with d|N
    """
    N = ZZ(N)
    d = ZZ(d)
    if N <= 0 or d <= 0 or (N % d) != 0:
        raise ValueError("N and d must be positive integers with d|N")

    return euler_phi(gcd(d, N//d))
Example #24
0
def num_cusps_of_width(N, d):
    r"""
    Return the number of cusps on `X_0(N)` of width d.

    INPUT:


    -  ``N`` - (integer): the level

    -  ``d`` - (integer): an integer dividing N, the cusp
       width


    EXAMPLES::

        sage: [num_cusps_of_width(18,d) for d in divisors(18)]
        [1, 1, 2, 2, 1, 1]
        sage: num_cusps_of_width(4,8)
        Traceback (most recent call last):
        ...
        ValueError: N and d must be positive integers with d|N
    """
    N = ZZ(N)
    d = ZZ(d)
    if N <= 0 or d <= 0 or (N % d) != 0:
        raise ValueError("N and d must be positive integers with d|N")

    return euler_phi(gcd(d, N // d))
Example #25
0
    def eval(self, expansion, weight=None):
        if weight is None:
            try:
                weight = expansion.weight()
            except AttributeError:
                raise ValueError, "weight must be defined for the Hecke action"

        precision = expansion.precision()
        if precision.is_infinite():
            precision = expansion._bounding_precision()
        else:
            precision = precision._hecke_operator(self.__l)
        characters = expansion.non_zero_components()
        expansion_level = precision.level()

        if gcd(expansion_level, self.__l) != 1:
            raise ValueError, "Level of expansion and of Hecke operator must be coprime."

        hecke_expansion = dict()
        for ch in characters:
            hecke_expansion[ch] = dict(
                (k,
                 self.hecke_coeff(expansion, ch, k, weight, expansion_level))
                for k in precision)

        result = expansion.parent()._element_constructor_(hecke_expansion)
        result._set_precision(expansion.precision()._hecke_operator(self.__l))

        return result
Example #26
0
def circle_drops(A,B):
    # Drops going around the unit circle for those A and B.
    # See http://user.math.uzh.ch/dehaye/thesis_students/Nicolas_Wider-Integrality_of_factorial_ratios.pdf
    # for longer description (not original, better references exist)    
    marks = lcm(lcm(A),lcm(B))    
    tmp = [0 for i in range(marks)]
    for a in A:
#        print tmp
        for i in range(a):
            if gcd(i, a) == 1:
                tmp[i*marks/a] -= 1
    for b in B:
#        print tmp
        for i in range(b):
            if gcd(i, b) == 1:
                tmp[i*marks/b] += 1
#    print tmp
    return [sum(tmp[:j]) for j in range(marks)]
Example #27
0
    def __init__(self, parent, polys, check=True):
        """
        The Python constructor.

        See :class:`SchemeMorphism_polynomial` for details.

        EXAMPLES::

            sage: A2.<x,y> = AffineSpace(QQ,2)
            sage: H = A2.Hom(A2)
            sage: H([x-y, x*y])
            Scheme endomorphism of Affine Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x, y) to
                    (x - y, x*y)
        """
        if check:
            if not isinstance(polys, (list, tuple)):
                raise TypeError, "polys (=%s) must be a list or tuple" % polys
            source_ring = parent.domain().coordinate_ring()
            target = parent.codomain().ambient_space()
            if len(polys) != target.ngens():
                raise ValueError, "there must be %s polynomials" % target.ngens(
                )
            try:
                polys = [source_ring(poly) for poly in polys]
            except TypeError:
                raise TypeError, "polys (=%s) must be elements of %s" % (
                    polys, source_ring)
            from sage.rings.quotient_ring import QuotientRing_generic
            if isinstance(source_ring, QuotientRing_generic):
                lift_polys = [f.lift() for f in polys]
            else:
                lift_polys = polys
            from sage.schemes.generic.projective_space import is_ProjectiveSpace
            if is_ProjectiveSpace(target):
                # if the codomain is a subscheme of projective space,
                # then we need to make sure that polys have no common
                # zeros
                if isinstance(source_ring, QuotientRing_generic):
                    # if the coordinate ring of the domain is a
                    # quotient by an ideal, we need to check that the
                    # gcd of polys and the generators of the ideal is 1
                    gcd_polys = lift_polys + list(
                        source_ring.defining_ideal().gens())
                else:
                    # if the domain is affine space, we just need to
                    # check the gcd of polys
                    gcd_polys = polys
                from sage.rings.arith import gcd
                if gcd(gcd_polys) != 1:
                    raise ValueError, "polys (=%s) must not have common factors" % polys
            polys = Sequence(lift_polys)
            polys.set_immutable()
            # Todo: check that map is well defined (how?)
        self.__polys = polys
        SchemeMorphism.__init__(self, parent)
Example #28
0
def blift(LF,Li,p,S=None):
    r"""
    Search for a solution to the given list of inequalities. If found, lift the solution to
    an appropriate valuation. See Lemma 3.3.6 in [Molnar, M.Sc. thesis]

    INPUT:

    - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients)

    - ``Li`` -- an integer, the bound on coefficients

    - ``p`` -- a prime

    OUTPUT:

    - Boolean -- whether or not the lift is successful

    - integer -- the lift

    EXAMPLES::

        sage: R.<b> = PolynomialRing(QQ)
        sage: from sage.schemes.projective.endPN_minimal_model import blift
        sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3)
        (True, 4)
    """

    P = LF[0].parent()
    #Determine which inequalities are trivial, and scale the rest, so that we only lift
    #as many times as needed.
    keepScaledIneqs = [scale(P(coeff),Li,p) for coeff in LF if coeff != 0]
    keptVals = [i[2] for i in keepScaledIneqs if i[0]]
    if keptVals != []:
        #Determine the valuation to lift until.
        liftval = max(keptVals)
    else:
        #All inequalities are satisfied.
        return True,1
    if S is None:
        S = PolynomialRing(Zmod(p),'b')
    keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]]
    #We need a solution for each polynomial on the left hand side of the inequalities,
    #so we need only find a solution for their gcd.
    g = gcd(keptScaledIneqs)
    rts = g.roots(multiplicities=False)
    for r in rts:
        #Recursively try to lift each root
        r_initial=QQ(r)
        newInput = P([r_initial,p])
        LG = [F(newInput) for F in LF]
        lift,lifted = blift(LG,Li,p,S=S)
        if lift:
            #Lift successful.
            return True,r_initial+ p*lifted
    #Lift non successful.
    return False,0
def blift(LF, Li, p, S=None):
    r"""
    Search for a solution to the given list of inequalities. If found, lift the solution to
    an appropriate valuation. See Lemma 3.3.6 in [Molnar, M.Sc. thesis]

    INPUT:

    - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients)

    - ``Li`` -- an integer, the bound on coefficients

    - ``p`` -- a prime

    OUTPUT:

    - Boolean -- whether or not the lift is successful

    - integer -- the lift

    EXAMPLES::

        sage: R.<b> = PolynomialRing(QQ)
        sage: from sage.schemes.projective.endPN_minimal_model import blift
        sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3)
        (True, 4)
    """

    P = LF[0].parent()
    #Determine which inequalities are trivial, and scale the rest, so that we only lift
    #as many times as needed.
    keepScaledIneqs = [scale(P(coeff), Li, p) for coeff in LF if coeff != 0]
    keptVals = [i[2] for i in keepScaledIneqs if i[0]]
    if keptVals != []:
        #Determine the valuation to lift until.
        liftval = max(keptVals)
    else:
        #All inequalities are satisfied.
        return True, 1
    if S is None:
        S = PolynomialRing(Zmod(p), 'b')
    keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]]
    #We need a solution for each polynomial on the left hand side of the inequalities,
    #so we need only find a solution for their gcd.
    g = gcd(keptScaledIneqs)
    rts = g.roots(multiplicities=False)
    for r in rts:
        #Recursively try to lift each root
        r_initial = QQ(r)
        newInput = P([r_initial, p])
        LG = [F(newInput) for F in LF]
        lift, lifted = blift(LG, Li, p, S=S)
        if lift:
            #Lift successful.
            return True, r_initial + p * lifted
    #Lift non successful.
    return False, 0
Example #30
0
 def arith_prod_of_partitions(l1, l2):
     # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
     # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
     # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
     # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
     # partition of `nm`.
     term_iterable = chain.from_iterable( repeat(lcm(pair), times=gcd(pair)) for pair in product(l1, l2) )
     term_list = sorted(term_iterable, reverse=True)
     res = Partition(term_list)
     return res
Example #31
0
 def arith_prod_of_partitions(l1, l2):
     # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by
     # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes
     # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2`
     # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a
     # partition of `nm`.
     term_iterable = chain.from_iterable( repeat(lcm(pair), times=gcd(pair)) for pair in product(l1, l2) )
     term_list = sorted(term_iterable, reverse=True)
     res = Partition(term_list)
     return res
    def __contains__(self, f):
        if self.__disc is infinity:
            return True

        (a, b, c) = f
        disc = 4 * a * c - b**2
        if disc == 0:
            return gcd([a, b, c]) < self._indefinite_content_bound()
        else:
            return disc < self.__disc
 def __contains__(self, f) :
     if self.__disc is infinity :
         return True
     
     (a, b, c) = f
     disc = 4*a*c - b**2
     if disc == 0 :
         return gcd([a, b, c]) < self._indefinite_content_bound()
     else :
         return disc < self.__disc
Example #34
0
def circle_drops(A, B):
    # Drops going around the unit circle for those A and B.
    # See http://user.math.uzh.ch/dehaye/thesis_students/Nicolas_Wider-Integrality_of_factorial_ratios.pdf
    # for longer description (not original, better references exist)
    marks = lcm(lcm(A), lcm(B))
    tmp = [0 for i in range(marks)]
    for a in A:
        #        print tmp
        for i in range(a):
            if gcd(i, a) == 1:
                tmp[i * marks / a] -= 1
    for b in B:
        #        print tmp
        for i in range(b):
            if gcd(i, b) == 1:
                tmp[i * marks / b] += 1


#    print tmp
    return [sum(tmp[:j]) for j in range(marks)]
Example #35
0
    def _new_group_from_level(self, level):
        r"""
        Return a new group of the same type (Gamma0, Gamma1, or
        GammaH) as self of the given level. In the case that self is of type
        GammaH, we take the largest H inside `(\ZZ/ \text{level}\ZZ)^\times`
        which maps to H, namely its inverse image under the natural reduction
        map.

        EXAMPLES::

            sage: G = Gamma0(20)
            sage: G._new_group_from_level(4)
            Congruence Subgroup Gamma0(4)
            sage: G._new_group_from_level(40)
            Congruence Subgroup Gamma0(40)

            sage: G = Gamma1(10)
            sage: G._new_group_from_level(6)
            Traceback (most recent call last):
            ...
            ValueError: one level must divide the other

            sage: G = GammaH(50,[7]); G
            Congruence Subgroup Gamma_H(50) with H generated by [7]
            sage: G._new_group_from_level(25)
            Congruence Subgroup Gamma_H(25) with H generated by [7]
            sage: G._new_group_from_level(10)
            Congruence Subgroup Gamma0(10)
            sage: G._new_group_from_level(100)
            Congruence Subgroup Gamma_H(100) with H generated by [7, 57]
        """
        from congroup_gamma0 import is_Gamma0
        from congroup_gamma1 import is_Gamma1
        from congroup_gammaH import is_GammaH
        from all import Gamma0, Gamma1, GammaH

        N = self.level()
        if (level % N) and (N % level):
            raise ValueError, "one level must divide the other"
        if is_Gamma0(self):
            return Gamma0(level)
        elif is_Gamma1(self):
            return Gamma1(level)
        elif is_GammaH(self):
            H = self._generators_for_H()
            if level > N:
                d = level // N
                diffs = [N * i for i in range(d)]
                newH = [h + diff for h in H for diff in diffs]
                return GammaH(level, [x for x in newH if gcd(level, x) == 1])
            else:
                return GammaH(level, [h % level for h in H])
        else:
            raise NotImplementedError
Example #36
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbb{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on
            `\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d
            \sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,
            N/d)`.

        EXAMPLES::

            sage: Gamma0(90)._find_cusps()
            [0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity]
            sage: Gamma0(1).cusps()
            [Infinity]
            sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')
            True
        """
        N = self.level()
        s = []

        for d in arith.divisors(N):
            w = arith.gcd(d, N//d)
            if w == 1:
                if d == 1:
                    s.append(Cusp(1,0))
                elif d == N:
                    s.append(Cusp(0,1))
                else:
                    s.append(Cusp(1,d))
            else:
                for a in xrange(1, w):
                    if arith.gcd(a, w) == 1:
                        while arith.gcd(a, d//w) != 1:
                            a += w
                        s.append(Cusp(a,d))
        return sorted(s)
Example #37
0
    def _find_cusps(self):
        r"""
        Return an ordered list of inequivalent cusps for self, i.e. a
        set of representatives for the orbits of self on
        `\mathbb{P}^1(\QQ)`.  These are returned in a reduced
        form; see self.reduce_cusp for the definition of reduced.

        ALGORITHM:
            Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on
            `\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d
            \sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d,
            N/d)`.

        EXAMPLES::

            sage: Gamma0(90)._find_cusps()
            [0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity]
            sage: Gamma0(1).cusps()
            [Infinity]
            sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym')
            True
        """
        N = self.level()
        s = []

        for d in arith.divisors(N):
            w = arith.gcd(d, N // d)
            if w == 1:
                if d == 1:
                    s.append(Cusp(1, 0))
                elif d == N:
                    s.append(Cusp(0, 1))
                else:
                    s.append(Cusp(1, d))
            else:
                for a in xrange(1, w):
                    if arith.gcd(a, w) == 1:
                        while arith.gcd(a, d // w) != 1:
                            a += w
                        s.append(Cusp(a, d))
        return sorted(s)
Example #38
0
    def eval_twisted_symbol_on_Da(self, a): # rename! should this be in modsym?
        """
        Returns `\Phi_{\chi}(\{a/p}-{\infty})` where `Phi` is the OMS and
        `\chi` is a the quadratic character corresponding to self

        INPUT:
            - ``a`` -- integer in range(p)

        OUTPUT:

        The distribution `\Phi_{\chi}(\{a/p\}-\{\infty\})`.

        EXAMPLES:
            sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve
            sage: E = EllipticCurve('57a')
            sage: p = 5
            sage: prec = 4
            sage: phi = ps_modsym_from_elliptic_curve(E)
            sage: ap = phi.Tq_eigenvalue(p,prec)
            sage: Phi = phi.p_stabilize_and_lift(p,ap = ap, M = prec, algorithm='stevens')
            sage: L = pAdicLseries(Phi)
            sage: L.eval_twisted_symbol_on_Da(1)
            (2 + 2*5 + 2*5^2 + 2*5^3 + O(5^4), 2 + 3*5 + 2*5^2 + O(5^3), 4*5 + O(5^2), 3 + O(5))

            sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve
            sage: E = EllipticCurve('40a4')
            sage: p = 7
            sage: prec = 4
            sage: phi = ps_modsym_from_elliptic_curve(E)
            sage: ap = phi.Tq_eigenvalue(p,prec)
            sage: Phi = phi.p_stabilize_and_lift(p,ap = ap, M = prec, algorithm='stevens')
            sage: L = pAdicLseries(Phi)
            sage: L.eval_twisted_symbol_on_Da(1)
            (4 + 6*7 + 3*7^2 + O(7^4), 2 + 7 + O(7^3), 4 + 6*7 + O(7^2), 6 + O(7))

        """
        symb = self.symb()
        p = symb.parent().prime()
        S0p = Sigma0(p)
        Dists = symb.parent().coefficient_module()
        M = Dists.precision_cap()
        p = Dists.prime()
        twisted_dist = Dists.zero_element()
        m_map = symb._map
        D = self._quadratic_twist
        for b in range(1, abs(D) + 1):
            if gcd(b, D) == 1:
                M1 = S0p([1, (b / abs(D)) % p**M, 0, 1])
                new_dist = m_map(M1 * M2Z([a, 1, p, 0]))*M1
                new_dist = new_dist.scale(kronecker(D, b)).normalize()
                twisted_dist = twisted_dist + new_dist
                #ans = ans + self.eval(M1 * M2Z[a, 1, p, 0])._right_action(M1)._lmul_(kronecker(D, b)).normalize()
        return twisted_dist.normalize()
 def _kohnen_rho(self, t, a) :
     dt = (-1)**(t.nrows() // 2) * t.det()
     d = fundamental_discriminant(dt)
     eps = isqrt(dt // d)
     
     res = 1
     for (p,e) in gcd(a, eps).factor() :
         pol = self._kohnen_rho_polynomial(t, p).coefficients()
         if e < len(pol) :
             res = res * pol[e]
         
     return res
Example #40
0
    def _new_group_from_level(self, level):
        r"""
        Return a new group of the same type (Gamma0, Gamma1, or
        GammaH) as self of the given level. In the case that self is of type
        GammaH, we take the largest H inside `(\ZZ/ \text{level}\ZZ)^\times`
        which maps to H, namely its inverse image under the natural reduction
        map.

        EXAMPLES::

            sage: G = Gamma0(20)
            sage: G._new_group_from_level(4)
            Congruence Subgroup Gamma0(4)
            sage: G._new_group_from_level(40)
            Congruence Subgroup Gamma0(40)

            sage: G = Gamma1(10)
            sage: G._new_group_from_level(6)
            Traceback (most recent call last):
            ...
            ValueError: one level must divide the other

            sage: G = GammaH(50,[7]); G
            Congruence Subgroup Gamma_H(50) with H generated by [7]
            sage: G._new_group_from_level(25)
            Congruence Subgroup Gamma_H(25) with H generated by [7]
            sage: G._new_group_from_level(10)
            Congruence Subgroup Gamma0(10)
            sage: G._new_group_from_level(100)
            Congruence Subgroup Gamma_H(100) with H generated by [7, 57]
        """
        from congroup_gamma0 import is_Gamma0
        from congroup_gamma1 import is_Gamma1
        from congroup_gammaH import is_GammaH
        from all import Gamma0, Gamma1, GammaH
        N = self.level()
        if (level % N) and (N % level):
            raise ValueError, "one level must divide the other"
        if is_Gamma0(self):
            return Gamma0(level)
        elif is_Gamma1(self):
            return Gamma1(level)
        elif is_GammaH(self):
            H = self._generators_for_H()
            if level > N:
                d = level // N
                diffs = [N * i for i in range(d)]
                newH = [h + diff for h in H for diff in diffs]
                return GammaH(level, [x for x in newH if gcd(level, x) == 1])
            else:
                return GammaH(level, [h % level for h in H])
        else:
            raise NotImplementedError
Example #41
0
    def __init__(self, parent, polys, check=True):
        """
        The Python constructor.

        See :class:`SchemeMorphism_polynomial` for details.

        EXAMPLES::

            sage: A2.<x,y> = AffineSpace(QQ,2)
            sage: H = A2.Hom(A2)
            sage: H([x-y, x*y])
            Scheme endomorphism of Affine Space of dimension 2 over Rational Field
              Defn: Defined on coordinates by sending (x, y) to
                    (x - y, x*y)
        """
        if check:
            if not isinstance(polys, (list, tuple)):
                raise TypeError, "polys (=%s) must be a list or tuple"%polys
            source_ring = parent.domain().coordinate_ring()
            target = parent.codomain().ambient_space()
            if len(polys) != target.ngens():
                raise ValueError, "there must be %s polynomials"%target.ngens()
            try:
                polys = [source_ring(poly) for poly in polys]
            except TypeError:
                raise TypeError, "polys (=%s) must be elements of %s"%(polys,source_ring)
            from sage.rings.quotient_ring import QuotientRing_generic
            if isinstance(source_ring, QuotientRing_generic):
                lift_polys = [f.lift() for f in polys]
            else:
                lift_polys = polys
            from sage.schemes.generic.projective_space import is_ProjectiveSpace
            if is_ProjectiveSpace(target):
                # if the codomain is a subscheme of projective space,
                # then we need to make sure that polys have no common
                # zeros 
                if isinstance(source_ring, QuotientRing_generic):
                    # if the coordinate ring of the domain is a
                    # quotient by an ideal, we need to check that the
                    # gcd of polys and the generators of the ideal is 1
                    gcd_polys = lift_polys + list(source_ring.defining_ideal().gens())
                else:
                    # if the domain is affine space, we just need to
                    # check the gcd of polys
                    gcd_polys = polys
                from sage.rings.arith import gcd
                if gcd(gcd_polys) != 1:
                    raise ValueError, "polys (=%s) must not have common factors"%polys
            polys = Sequence(lift_polys)
            polys.set_immutable()
            # Todo: check that map is well defined (how?)
        self.__polys = polys
        SchemeMorphism.__init__(self, parent)
    def _kohnen_rho(self, t, a):
        dt = (-1)**(t.nrows() // 2) * t.det()
        d = fundamental_discriminant(dt)
        eps = isqrt(dt // d)

        res = 1
        for (p, e) in gcd(a, eps).factor():
            pol = self._kohnen_rho_polynomial(t, p).coefficients()
            if e < len(pol):
                res = res * pol[e]

        return res
Example #43
0
    def _test__jacobi_taylor_coefficients(expansion, weight, prec=None):
        r"""
        Compute the renormalized Taylor coefficients of
        a Jacobi form.
        
        INPUT:
        
        - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys.
        
        - ``weight`` -- An integer.
        
        - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion
                      possibly ``None``.
        
        OUTPUT:
        
        - A list of power series in `q`.
        """
        from sage.rings.arith import gcd

        if prec is None:
            prec = expansion.precision()
        jacobi_index = prec.jacobi_index()
        q_precision = prec.index()
        R = PowerSeriesRing(ZZ, 'q')
        q = R.gen(0)

        weak_prec = JacobiFormD1NNFilter(prec, reduced=False, weak_forms=True)
        indices = JacobiFormD1NNIndices(jacobi_index)

        projs = list()
        for pw in (range(0, 2 * jacobi_index + 1, 2) if weight %
                   2 == 0 else range(1, 2 * jacobi_index - 1, 2)):
            proj = dict((n, 0) for n in range(q_precision))
            for (n, r) in weak_prec:
                ((nred, rred), sign) = indices.reduce((n, r))
                try:
                    proj[n] += (sign * r)**pw * expansion[(nred, rred)]
                except (KeyError, ValueError):
                    pass

            projs.append(proj)

        gcd_projs = [gcd(proj.values()) for proj in projs]
        gcd_projs = [g if g != 0 else 1 for g in gcd_projs]
        projs = [sorted(proj.iteritems()) for proj in projs]
        projs = [
            R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj
            for (proj, gcd_proj) in zip(projs, gcd_projs)
        ]

        return projs
Example #44
0
    def ncusps(self):
        r"""
        Return the number of cusps of this subgroup `\Gamma_0(N)`.

        EXAMPLES::

            sage: [Gamma0(n).ncusps() for n in [1..19]]
            [1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2]
            sage: [Gamma0(n).ncusps() for n in prime_range(2,100)]
            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
        """
        n = self.level()
        return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])
Example #45
0
    def ncusps(self):
        r"""
        Return the number of cusps of this subgroup `\Gamma_0(N)`.

        EXAMPLES::
            
            sage: [Gamma0(n).ncusps() for n in [1..19]]
            [1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2]
            sage: [Gamma0(n).ncusps() for n in prime_range(2,100)]
            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
        """
        n = self.level()
        return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])
Example #46
0
    def _mul_(self, other):
        """
        Multiply this Hecke operator by another element of the same algebra. If
        the other element is of the form `T_m` for some m, we check whether the
        product is equal to `T_{mn}` and return that; if the product is not
        (easily seen to be) of the form `T_{mn}`, then we calculate the product
        of the two matrices and return a Hecke algebra element defined by that.

        EXAMPLES: We create the space of modular symbols of level
        `11` and weight `2`, then compute `T_2`
        and `T_3` on it, along with their composition.

        ::

            sage: M = ModularSymbols(11)
            sage: t2 = M.hecke_operator(2); t3 = M.hecke_operator(3)
            sage: t2*t3 # indirect doctest
            Hecke operator T_6 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field
            sage: t3.matrix() * t2.matrix()
            [12  0 -2]
            [ 0  2  0]
            [ 0  0  2]
            sage: (t2*t3).matrix()
            [12  0 -2]
            [ 0  2  0]
            [ 0  0  2]

        When we compute `T_2^5` the result is not (easily seen to
        be) a Hecke operator of the form `T_n`, so it is returned
        as a Hecke module homomorphism defined as a matrix::

            sage: t2**5
            Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by:
            [243   0 -55]
            [  0 -32   0]
            [  0   0 -32]
        """
        if isinstance(other,
                      HeckeOperator) and other.parent() == self.parent():
            n = None
            if arith.gcd(self.__n, other.__n) == 1:
                n = self.__n * other.__n
            else:
                P = set(arith.prime_divisors(self.domain().level()))
                if P.issubset(set(arith.prime_divisors(self.__n))) and \
                   P.issubset(set(arith.prime_divisors(other.__n))):
                    n = self.__n * other.__n
            if n:
                return HeckeOperator(self.parent(), n)
        # otherwise
        return self.matrix_form() * other
Example #47
0
    def _iter_positive_forms_with_content_and_discriminant(self):
        if self.__disc is infinity:
            raise ValueError, "infinity is not a true filter index"

        if self.__reduced:
            for (l, (u, x)) in enumerate(self.__p1list):
                if u == 0:
                    for a in xrange(self.__level,
                                    isqrt(self.__disc // 4) + 1, self.__level):
                        frpa = 4 * a
                        for b in xrange(a + 1):
                            g = gcd(a // self.__level, b)
                            bsq = b**2

                            for c in xrange(a, (b**2 + (self.__disc - 1)) //
                                            (4 * a) + 1):
                                yield (((a, b, c), l), gcd(g,
                                                           c), frpa * c - bsq)
                else:
                    for a in xrange(1, isqrt(self.__disc // 3) + 1):
                        frpa = 4 * a
                        for b in xrange(a + 1):
                            g = gcd(a, b)
                            bsq = b**2

                            ## We need x**2 * a + x * b + c % N == 0
                            h = (-((x**2 + 1) * a + x * b)) % self.__level
                            for c in xrange(a + h,
                                            (b**2 + (self.__disc - 1)) //
                                            (4 * a) + 1, self.__level):
                                yield (((a, b, c), l),
                                       gcd(g, (x**2 * a + x * b + c) //
                                           self.__level), frpa * c - bsq)
        #! if self.__reduced
        else:
            raise NotImplementedError

        raise StopIteration
Example #48
0
    def _mul_(self, other):
        """
        Multiply this Hecke operator by another element of the same algebra. If
        the other element is of the form `T_m` for some m, we check whether the
        product is equal to `T_{mn}` and return that; if the product is not
        (easily seen to be) of the form `T_{mn}`, then we calculate the product
        of the two matrices and return a Hecke algebra element defined by that.

        EXAMPLES: We create the space of modular symbols of level
        `11` and weight `2`, then compute `T_2`
        and `T_3` on it, along with their composition.

        ::

            sage: M = ModularSymbols(11)
            sage: t2 = M.hecke_operator(2); t3 = M.hecke_operator(3)
            sage: t2*t3 # indirect doctest
            Hecke operator T_6 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field
            sage: t3.matrix() * t2.matrix()
            [12  0 -2]
            [ 0  2  0]
            [ 0  0  2]
            sage: (t2*t3).matrix()
            [12  0 -2]
            [ 0  2  0]
            [ 0  0  2]

        When we compute `T_2^5` the result is not (easily seen to
        be) a Hecke operator of the form `T_n`, so it is returned
        as a Hecke module homomorphism defined as a matrix::

            sage: t2**5
            Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by:
            [243   0 -55]
            [  0 -32   0]
            [  0   0 -32]
        """
        if isinstance(other, HeckeOperator) and other.parent() == self.parent():
            n = None
            if arith.gcd(self.__n, other.__n) == 1:
                n = self.__n * other.__n
            else:
                P = set(arith.prime_divisors(self.domain().level()))
                if P.issubset(set(arith.prime_divisors(self.__n))) and \
                   P.issubset(set(arith.prime_divisors(other.__n))):
                    n = self.__n * other.__n
            if n:
                return HeckeOperator(self.parent(), n)
        # otherwise
        return self.matrix_form() * other
def basiclemma(self, M):
    """
    Finds a number represented by self and coprime to M.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 2, [2, 1, 3])
        sage: Q.basiclemma(6)
        71

    """
    a = self(self.basiclemmavec(M))
    assert gcd(a, M) == 1
    return a
def basiclemma(self, M):
    """
    Finds a number represented by self and coprime to M.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 2, [2, 1, 3])
        sage: Q.basiclemma(6)
        71

    """
    a = self(self.basiclemmavec(M))
    assert gcd(a, M) == 1
    return a
Example #51
0
def circle_image(A,B):
    G = Graphics()
    G += circle((0,0), 1 , color = 'grey')
    from collections import defaultdict
    tmp = defaultdict(int)
    for a in A:
        for j in range(a):
            if gcd(j,a) == 1:
                rational = Rational(j)/Rational(a)
                tmp[(rational.numerator(),rational.denominator())] += 1
    
    for b in B:
        for j in range(b):
            if gcd(j,b) == 1:
                rational = Rational(j)/Rational(b)
                tmp[(rational.numerator(),rational.denominator())] -= 1
    C = ComplexField()
    for val in tmp:
        if tmp[val] > 0:
            G += text(str(tmp[val]),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "green")
        if tmp[val] < 0:
            G += text(str(abs(tmp[val])),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "red")
    return G
    def _test__jacobi_taylor_coefficients(expansion, weight, prec = None) :
        r"""
        Compute the renormalized Taylor coefficients of
        a Jacobi form.
        
        INPUT:
        
        - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys.
        
        - ``weight`` -- An integer.
        
        - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion
                      possibly ``None``.
        
        OUTPUT:
        
        - A list of power series in `q`.
        """
        from sage.rings.arith import gcd
        
        if prec is None :
            prec = expansion.precision()
        jacobi_index = prec.jacobi_index()
        q_precision = prec.index()
        R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0)
                
        weak_prec = JacobiFormD1NNFilter(prec, reduced = False, weak_forms = True)
        indices = JacobiFormD1NNIndices(jacobi_index)

        projs = list()
        for pw in (range(0, 2 * jacobi_index + 1, 2) if weight % 2 == 0 else range(1, 2 * jacobi_index - 1, 2)) :
            proj = dict( (n, 0) for n in range(q_precision) )
            for (n, r) in weak_prec :
                ((nred, rred), sign) = indices.reduce((n,r))
                try :
                    proj[n] +=  (sign * r)**pw * expansion[(nred, rred)]
                except (KeyError, ValueError) :
                    pass
            
            projs.append(proj)

        gcd_projs = [gcd(proj.values()) for proj in projs]
        gcd_projs = [g if g != 0 else 1 for g in gcd_projs]
        projs = [sorted(proj.iteritems()) for proj in projs]
        projs = [ R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj
                  for (proj, gcd_proj) in zip(projs, gcd_projs) ]
        
        return projs
Example #53
0
File: gpc.py Project: Fougeroc/lekz
def order_mod_n(k,n):
    r"""
    Return the (additive) order of ``k`` mod ``n`` which is ``n/gcd(k,n)``.

    EXAMPLES::

        sage: order_mod_n(0,2)
        1
        sage: order_mod_n(1,2)
        2
        sage: [order_mod_n(i,4) for i in xrange(4)]
        1 4 2 4
    """
    if k == 0:
        return 1
    return n // gcd(k,n)
Example #54
0
    def cardinality(self):
        r"""
        Return the number of integer necklaces with the evaluation ``content``.

        The formula for the number of necklaces of content `\alpha`
        a composition of `n` is:

        .. MATH::

            \sum_{d|gcd(\alpha)} \phi(d)
            \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d},

        where `\phi(d)` is the Euler `\phi` function.

        EXAMPLES::

            sage: Necklaces([]).cardinality()
            0
            sage: Necklaces([2,2]).cardinality()
            2
            sage: Necklaces([2,3,2]).cardinality()
            30
            sage: Necklaces([0,3,2]).cardinality()
            2

        Check to make sure that the count matches up with the number of
        necklace words generated.

        ::

            sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list()
            sage: ns = [ Necklaces(comp) for comp in comps]
            sage: all( [ n.cardinality() == len(n.list()) for n in ns] )
            True
        """
        evaluation = self._content
        le = list(evaluation)
        if not le:
            return 0

        n = sum(le)

        return sum(
            euler_phi(j) * factorial(n / j) /
            prod(factorial(ni / j) for ni in evaluation)
            for j in divisors(gcd(le))) / n
Example #55
0
    def _Weyl_law_consts(self):
        r"""
        Compute constants for the Weyl law on self._G

        OUTPUT:

        - tuple of real numbers

        EXAMPLES::


            sage: M=MaassWaveForms(MySubgroup(Gamma0(1)))
            sage: M._Weyl_law_consts()
            (0, 2/pi, (log(pi) - log(2) + 2)/pi, 0, -2)
        """
        import mpmath
        pi=mpmath.fp.pi
        ix=Integer(self._G.index())
        nc=Integer(len(self._G.cusps()))
        if(self._G.is_congruence()):
            lvl=Integer(self._G.level())
        else:
            lvl=0
        n2=Integer(self._G.nu2())
        n3=Integer(self._G.nu3())
        c1=ix/Integer(12)
        c2=Integer(2)*nc/pi
        c3=nc*(Integer(2)-ln(Integer(2))+ln(pi))/pi
        if(lvl<>0):
            A=1
            for q in divisors(lvl):
                num_prim_dc=0
                DG=DirichletGroup(q)
                for chi in DG.list():
                    if(chi.is_primitive()):
                        num_prim_dc=num_prim_dc+1
                for m in divisors(lvl):
                    if(lvl % (m*q) == 0   and m % q ==0 ): 
                        fak=(q*lvl)/gcd(m,lvl/m)
                        A=A*Integer(fak)**num_prim_dc        
            c4=-ln(A)/pi
        else:
            c4=Integer(0)
        # constant term
        c5=-ix/144+n2/8+n3*2/9-nc/4-1
        return (c1,c2,c3,c4,c5)
Example #56
0
    def reduce(self, t):
        ## We compute the rational diagonal form of t. Whenever a zero entry occures we
        ## find a primitive isotropic vector and apply a base change, such that t finally
        ## has the form diag(0,0,...,P) where P is positive definite. P will then be a
        ## LLL reduced.

        ot = t
        t = matrix(QQ, t)
        n = t.nrows()
        u = identity_matrix(QQ, n)

        for i in xrange(n):
            if t[i, i] < 0:
                return None
            elif t[i, i] == 0:
                ## get the isotropic vector
                v = u.column(i)
                v = v.denominator() * v
                v = v / gcd(list(v))
                u = self._gln_lift(v, 0)

                t = u.transpose() * ot * u
                ts = self.reduce(t.submatrix(1, 1, n - 1, n - 1))
                if ts is None:
                    return None
                t.set_block(1, 1, ts[0])

                t.set_immutable()

                return (t, 1)
            else:
                for j in xrange(i + 1, n):
                    us = identity_matrix(QQ, n, n)
                    us[i, j] = -t[i, j] / t[i, i]
                    u = u * us

                    t.add_multiple_of_row(j, i, -t[j, i] / t[i, i])
                    t.add_multiple_of_column(j, i, -t[i, j] / t[i, i])

        u = ot.LLL_gram()
        t = u.transpose() * ot * u
        t.set_immutable()

        return (t, 1)
Example #57
0
    def cardinality(self):
        r"""
        Return the number of integer necklaces with the evaluation ``content``.

        The formula for the number of necklaces of content `\alpha`
        a composition of `n` is:

        .. MATH::

            \sum_{d|gcd(\alpha)} \phi(d)
            \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d},

        where `\phi(d)` is the Euler `\phi` function.

        EXAMPLES::

            sage: Necklaces([]).cardinality()
            0
            sage: Necklaces([2,2]).cardinality()
            2
            sage: Necklaces([2,3,2]).cardinality()
            30
            sage: Necklaces([0,3,2]).cardinality()
            2

        Check to make sure that the count matches up with the number of
        necklace words generated.

        ::

            sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list()
            sage: ns = [ Necklaces(comp) for comp in comps]
            sage: all( [ n.cardinality() == len(n.list()) for n in ns] )
            True
        """
        evaluation = self._content
        le = list(evaluation)
        if not le:
            return 0

        n = sum(le)

        return sum(euler_phi(j)*factorial(n/j) / prod(factorial(ni/j)
                    for ni in evaluation) for j in divisors(gcd(le))) / n
 def reduce(self, t) :
     ## We compute the rational diagonal form of t. Whenever a zero entry occures we
     ## find a primitive isotropic vector and apply a base change, such that t finally
     ## has the form diag(0,0,...,P) where P is positive definite. P will then be a
     ## LLL reduced.
     
     ot = t
     t = matrix(QQ, t)
     n = t.nrows()
     u = identity_matrix(QQ, n)
     
     for i in xrange(n) :
         if t[i,i] < 0 :
             return None
         elif t[i,i] == 0 :
             ## get the isotropic vector
             v = u.column(i)
             v = v.denominator() * v
             v = v / gcd(list(v))
             u = self._gln_lift(v, 0)
             
             t = u.transpose() * ot * u
             ts = self.reduce(t.submatrix(1,1,n-1,n-1))
             if ts is None :
                 return None
             t.set_block(1, 1, ts[0])
             
             t.set_immutable()
             
             return (t,1)
         else :
             for j in xrange(i + 1, n) :
                 us = identity_matrix(QQ, n, n)
                 us[i,j] = -t[i,j]/t[i,i]
                 u = u * us
                  
                 t.add_multiple_of_row(j, i, -t[j,i]/t[i,i])
                 t.add_multiple_of_column(j, i, -t[i,j]/t[i,i])
     
     u = ot.LLL_gram()
     t = u.transpose() * ot * u
     t.set_immutable()
     
     return (t, 1)
Example #59
0
 def hecke_operator(self, n):
     """
     Return the `n`-th Hecke operator, for `n` any
     positive integer coprime to the level.
     
     EXAMPLES::
     
         sage: T = ModularSymbols(Gamma1(5),3).anemic_hecke_algebra()
         sage: T.hecke_operator(2)
         Hecke operator T_2 on Modular Symbols space of dimension 4 for Gamma_1(5) of weight 3 with sign 0 and over Rational Field
         sage: T.hecke_operator(5)
         Traceback (most recent call last):
         ...
         IndexError: Hecke operator T_5 not defined in the anemic Hecke algebra
     """
     n = int(n)
     if arith.gcd(self.module().level(), n) != 1:
         raise IndexError, "Hecke operator T_%s not defined in the anemic Hecke algebra" % n
     return self.module()._hecke_operator_class()(self, n)
Example #60
0
    def is_primitive(self):
        """
        Checks if the form `ax^2 + bxy + cy^2`  satisfies
        `\gcd(a,b,c)=1`, i.e., is primitive.

        EXAMPLES::


            sage: Q = BinaryQF([6,3,9])
            sage: Q.is_primitive()
            False

            sage: Q = BinaryQF([1,1,1])
            sage: Q.is_primitive()
            True

            sage: Q = BinaryQF([2,2,2])
            sage: Q.is_primitive()
            False

            sage: rqf = BinaryQF_reduced_representatives(-23*9)
            sage: [qf.is_primitive() for qf in rqf]
            [True, True, True, False, True, True, False, False, True]
            sage: rqf
            [x^2 + x*y + 52*y^2,
            2*x^2 - x*y + 26*y^2,
            2*x^2 + x*y + 26*y^2,
            3*x^2 + 3*x*y + 18*y^2,
            4*x^2 - x*y + 13*y^2,
            4*x^2 + x*y + 13*y^2,
            6*x^2 - 3*x*y + 9*y^2,
            6*x^2 + 3*x*y + 9*y^2,
            8*x^2 + 7*x*y + 8*y^2]
            sage: [qf for qf in rqf if qf.is_primitive()]
            [x^2 + x*y + 52*y^2,
            2*x^2 - x*y + 26*y^2,
            2*x^2 + x*y + 26*y^2,
            4*x^2 - x*y + 13*y^2,
            4*x^2 + x*y + 13*y^2,
            8*x^2 + 7*x*y + 8*y^2]
        """
        from sage.rings.arith import gcd
        return gcd([self._a, self._b, self._c]) == 1