Exemple #1
0
    def cardinality(self):
        """
        Returns the number of Lyndon words with the evaluation e.

        EXAMPLES::

            sage: LyndonWords([]).cardinality()
            0
            sage: LyndonWords([2,2]).cardinality()
            1
            sage: LyndonWords([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: lws = [ LyndonWords(comp) for comp in comps]
            sage: all( [ lw.cardinality() == len(lw.list()) for lw in lws] )
            True
        """
        evaluation = self.e
        le = __builtin__.list(evaluation)
        if len(evaluation) == 0:
            return 0

        n = sum(evaluation)

        return sum([moebius(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n
    def _cycle_type(self, s):
        """
        EXAMPLES::

            sage: cis = species.PartitionSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
            sage: cis = species.PermutationSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[3, 1, 1, 1], [2, 2, 1, 1], [1, 1, 1, 1, 1, 1]]
            sage: cis = species.SetSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[1], [1], [1]]
        """
        if s == []:
            return self._card(0)
        res = []
        for k in range(1, self._upper_bound_for_longest_cycle(s)+1):
            e = 0
            for d in divisors(k):
                m = moebius(d)
                if m == 0:
                    continue
                u = s.power(k/d)
                e += m*self.count(u)
            res.extend([k]*int(e/k))
        res.reverse()
        return Partition(res)
    def cardinality(self):
        """
        Returns the number of Lyndon words with the evaluation e.

        EXAMPLES::

            sage: LyndonWords([]).cardinality()
            0
            sage: LyndonWords([2,2]).cardinality()
            1
            sage: LyndonWords([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: lws = [ LyndonWords(comp) for comp in comps]
            sage: all( [ lw.cardinality() == len(lw.list()) for lw in lws] )
            True
        """
        evaluation = self.e
        le = __builtin__.list(evaluation)
        if len(evaluation) == 0:
            return 0

        n = sum(evaluation)

        return sum([
            moebius(j) * factorial(n / j) /
            prod([factorial(ni / j) for ni in evaluation])
            for j in divisors(gcd(le))
        ]) / n
    def _cycle_type(self, s):
        """
        EXAMPLES::

            sage: cis = species.PartitionSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
            sage: cis = species.PermutationSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[3, 1, 1, 1], [2, 2, 1, 1], [1, 1, 1, 1, 1, 1]]
            sage: cis = species.SetSpecies().cycle_index_series()
            sage: [cis._cycle_type(p) for p in Partitions(3)]
            [[1], [1], [1]]
        """
        if s == []:
            return self._card(0)
        res = []
        for k in range(1, self._upper_bound_for_longest_cycle(s) + 1):
            e = 0
            for d in divisors(k):
                m = moebius(d)
                if m == 0:
                    continue
                u = s.power(k / d)
                e += m * self.count(u)
            res.extend([k] * int(e / k))
        res.reverse()
        return Partition(res)
    def cardinality(self):
        """
        TESTS::

            sage: [ LyndonWords(3,i).cardinality() for i in range(1, 11) ]
            [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880]
        """
        if self.k == 0:
            return 1
        else:
            s = 0
            for d in divisors(self.k):
                s += moebius(d) * (self.n**(self.k / d))
        return s / self.k
Exemple #6
0
    def cardinality(self):
        """
        TESTS::

            sage: [ LyndonWords(3,i).cardinality() for i in range(1, 11) ]
            [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880]
        """
        if self.k == 0:
            return 1
        else:
            s = 0
            for d in divisors(self.k):
                s += moebius(d)*(self.n**(self.k/d))
        return s/self.k
Exemple #7
0
    def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of Eisenstein series forms for self,
        or the dimension of the subspace corresponding to the given character
        if one is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          Eisenstein series of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Moebius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        AUTHORS:

        - William Stein - Cohen--Oesterle algorithm

        - Jordi Quer - algorithm based on GammaH subgroups

        - David Loeffler (2009) - code refactoring

        EXAMPLES:

        The following two computations use different algorithms: ::

            sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]
            sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]

        So do these: ::

            sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
            sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
        """
        from all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_eis(self, k)

        N = self.level()
        eps = DirichletGroup(N)(eps)

        if eps.is_trivial():
            return Gamma0(N).dimension_eis(k)

        # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid:
        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):
            return ZZ(0)

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N,(eps**d).kernel())
                dim = dim + moebius(d)*G.dimension_eis(k)
            return dim//phi(n)

        elif algorithm == "CohenOesterle":
            from sage.modular.dims import CohenOesterle
            K = eps.base_ring()
            j = 2-k
            # We use the Cohen-Oesterle formula in a subtle way to
            # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on
            # computing with modular forms).
            alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) )
            if k == 1:
                return alpha
            else:
                return alpha - self.dimension_cusp_forms(k, eps)

        else: #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError, "Unrecognised algorithm in dimension_eis"
Exemple #8
0
    def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of cusp forms for self, or the
        dimension of the subspace corresponding to the given character if one
        is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          forms of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Moebius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        EXAMPLES:

        We compute the same dimension in two different ways ::

            sage: K = CyclotomicField(3)
            sage: eps = DirichletGroup(7*43,K).0^2
            sage: G = Gamma1(7*43)

        Via Cohen--Oesterle: ::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps)
            28

        Via Quer's method: ::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer")
            28

        Some more examples: ::

            sage: G.<eps> = DirichletGroup(9)
            sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]]
            [0, 0, 1, 0, 3, 0, 5, 0, 7, 0]
            sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]]
            [0, 0, 0, 2, 0, 4, 0, 6, 0, 8]
        """

        from all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_cusp_forms(self, k)

        N = self.level()
        if eps.base_ring().characteristic() != 0:
            raise ValueError

        eps = DirichletGroup(N, eps.base_ring())(eps)

        if eps.is_trivial():
            return Gamma0(N).dimension_cusp_forms(k)

        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()):
            return ZZ(0)

        if k == 1:
            try:
                n = self.dimension_cusp_forms(1)
                if n == 0:
                    return ZZ(0)
                else: # never happens at present
                    raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present"
            except NotImplementedError:
                raise

        # now the main part

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N,(eps**d).kernel())
                dim = dim + moebius(d)*G.dimension_cusp_forms(k)
            return dim//phi(n)

        elif algorithm == "CohenOesterle":
            K = eps.base_ring()
            from sage.modular.dims import CohenOesterle
            from all import Gamma0
            return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) )

        else: #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"
Exemple #9
0
    def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of Eisenstein series forms for self,
        or the dimension of the subspace corresponding to the given character
        if one is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          Eisenstein series of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Moebius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        AUTHORS:

        - William Stein - Cohen--Oesterle algorithm

        - Jordi Quer - algorithm based on GammaH subgroups

        - David Loeffler (2009) - code refactoring

        EXAMPLES:

        The following two computations use different algorithms: ::

            sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]
            sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)]
            [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0]

        So do these: ::

            sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
            sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)]
            [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0]
        """
        from all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_eis(self, k)

        N = self.level()
        eps = DirichletGroup(N)(eps)

        if eps.is_trivial():
            return Gamma0(N).dimension_eis(k)

        # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid:
        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0
                                                            and eps.is_odd()):
            return ZZ(0)

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N, (eps**d).kernel())
                dim = dim + moebius(d) * G.dimension_eis(k)
            return dim // phi(n)

        elif algorithm == "CohenOesterle":
            from sage.modular.dims import CohenOesterle
            K = eps.base_ring()
            j = 2 - k
            # We use the Cohen-Oesterle formula in a subtle way to
            # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on
            # computing with modular forms).
            alpha = -ZZ(
                K(Gamma0(N).index() *
                  (j - 1) / ZZ(12)) + CohenOesterle(eps, j))
            if k == 1:
                return alpha
            else:
                return alpha - self.dimension_cusp_forms(k, eps)

        else:  #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError, "Unrecognised algorithm in dimension_eis"
Exemple #10
0
    def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"):
        r"""
        Return the dimension of the space of cusp forms for self, or the
        dimension of the subspace corresponding to the given character if one
        is supplied.

        INPUT:

        - ``k`` - an integer (default: 2), the weight.

        - ``eps`` - either None or a Dirichlet character modulo N, where N is
          the level of this group. If this is None, then the dimension of the
          whole space is returned; otherwise, the dimension of the subspace of
          forms of character eps.

        - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This
          specifies the method to use in the case of nontrivial character:
          either the Cohen--Oesterle formula as described in Stein's book, or
          by Moebius inversion using the subgroups GammaH (a method due to
          Jordi Quer).

        EXAMPLES:

        We compute the same dimension in two different ways ::

            sage: K = CyclotomicField(3)
            sage: eps = DirichletGroup(7*43,K).0^2
            sage: G = Gamma1(7*43)

        Via Cohen--Oesterle: ::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps)
            28

        Via Quer's method: ::

            sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer")
            28

        Some more examples: ::

            sage: G.<eps> = DirichletGroup(9)
            sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]]
            [0, 0, 1, 0, 3, 0, 5, 0, 7, 0]
            sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]]
            [0, 0, 0, 2, 0, 4, 0, 6, 0, 8]
        """

        from all import Gamma0

        # first deal with special cases

        if eps is None:
            return GammaH_class.dimension_cusp_forms(self, k)

        N = self.level()
        if eps.base_ring().characteristic() != 0:
            raise ValueError

        eps = DirichletGroup(N, eps.base_ring())(eps)

        if eps.is_trivial():
            return Gamma0(N).dimension_cusp_forms(k)

        if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0
                                                            and eps.is_odd()):
            return ZZ(0)

        if k == 1:
            try:
                n = self.dimension_cusp_forms(1)
                if n == 0:
                    return ZZ(0)
                else:  # never happens at present
                    raise NotImplementedError, "Computations of dimensions of spaces of weight 1 cusp forms not implemented at present"
            except NotImplementedError:
                raise

        # now the main part

        if algorithm == "Quer":
            n = eps.order()
            dim = ZZ(0)
            for d in n.divisors():
                G = GammaH_constructor(N, (eps**d).kernel())
                dim = dim + moebius(d) * G.dimension_cusp_forms(k)
            return dim // phi(n)

        elif algorithm == "CohenOesterle":
            K = eps.base_ring()
            from sage.modular.dims import CohenOesterle
            from all import Gamma0
            return ZZ(
                K(Gamma0(N).index() * (k - 1) / ZZ(12)) +
                CohenOesterle(eps, k))

        else:  #algorithm not in ["CohenOesterle", "Quer"]:
            raise ValueError, "Unrecognised algorithm in dimension_cusp_forms"
def dimension__jacobi_scalar_f(k, m, f) :
    if moebius(f) != (-1)**k :
        return 0
    
    ## We use chapter 6 of Skoruppa's thesis
    ts = filter(lambda t: gcd(t, m // t) == 1, m.divisors())
    
    ## Eisenstein part
    eis_dimension = 0
    
    for t in ts :
        eis_dimension +=   moebius(gcd(m // t, f)) \
                        * (t // t.squarefree_part()).isqrt() \
                        * (2 if (m // t) % 4 == 0 else 1) 
    eis_dimension = eis_dimension // len(ts)
    
    if k == 2 and f == 1 :
        eis_dimension -= len( (m // m.squarefree_part()).isqrt().divisors() ) 
    
    ## Cuspidal part
    cusp_dimension = 0
    
    tmp = ZZ(0)
    for t in ts :
        tmp += moebius(gcd(m // t, f)) * t
    tmp = tmp / len(ts)
    cusp_dimension += tmp * (2 * k - 3) / ZZ(12)
    print "1: ", cusp_dimension
    
    if m % 2 == 0 :
        tmp = ZZ(0)
        for t in ts :
            tmp += moebius(gcd(m // t, f)) * kronecker_symbol(-4, t)
        tmp = tmp / len(ts)
        
        cusp_dimension += 1/ZZ(2) * kronecker_symbol(8, 2 * k - 1) * tmp
        print "2: ", 1/ZZ(2) * kronecker_symbol(8, 2 * k - 1) * tmp
        
    tmp = ZZ(0)
    for t in ts :
        tmp += moebius(gcd(m // t, f)) * kronecker_symbol(t, 3)
    tmp = tmp / len(ts)
    if m % 3 != 0 :
        cusp_dimension += 1 / ZZ(3) * kronecker_symbol(k, 3) * tmp
        print ": ", 1 / ZZ(3) * kronecker_symbol(k, 3) * tmp
    elif k % 3 == 0 :
        cusp_dimension += 2 / ZZ(3) * (-1)**k * tmp
        print "3: ", 2 / ZZ(3) * (-1)**k * tmp
    else :
        cusp_dimension += 1 / ZZ(3) * (kronecker_symbol(k, 3) + (-1)**(k - 1)) * tmp
        print "3: ", 1 / ZZ(3) * (kronecker_symbol(k, 3) + (-1)**(k - 1)) * tmp
    
    tmp = ZZ(0)
    for t in ts :
        tmp +=   moebius(gcd(m // t, f)) \
               * (t // t.squarefree_part()).isqrt() \
               * (2 if (m // t) % 4 == 0 else 1)
    tmp = tmp / len(ts)
    cusp_dimension -= 1 / ZZ(2) * tmp
    print "4: ", -1 / ZZ(2) * tmp
    
    tmp = ZZ(0)
    for t in ts :
        tmp +=   moebius(gcd(m // t, f)) \
               * sum(   (( len(BinaryQF_reduced_representatives(-d, True))
                           if d not in [3, 4] else ( 1 / ZZ(3) if d == 3 else 1 / ZZ(2) ))
                         if d % 4 == 0 or d % 4 == 3 else 0 )
                      * kronecker_symbol(-d, m // t)
                      * ( 1 if (m // t) % 2 != 0 else
                          ( 4 if (m // t) % 4 == 0 else 2 * kronecker_symbol(-d, 2) ))
                      for d in (4 * m).divisors() )
    tmp = tmp / len(ts)
    cusp_dimension -= 1 / ZZ(2) * tmp
    print "5: ", -1 / ZZ(2) * tmp
    
    if k == 2 :
        cusp_dimension += len( (m // f // (m // f).squarefree_part()).isqrt().divisors() )
    
    return eis_dimension + cusp_dimension
Exemple #12
0
def dimension__jacobi_scalar_f(k, m, f):
    if moebius(f) != (-1)**k:
        return 0

    ## We use chapter 6 of Skoruppa's thesis
    ts = filter(lambda t: gcd(t, m // t) == 1, m.divisors())

    ## Eisenstein part
    eis_dimension = 0

    for t in ts:
        eis_dimension +=   moebius(gcd(m // t, f)) \
                        * (t // t.squarefree_part()).isqrt() \
                        * (2 if (m // t) % 4 == 0 else 1)
    eis_dimension = eis_dimension // len(ts)

    if k == 2 and f == 1:
        eis_dimension -= len((m // m.squarefree_part()).isqrt().divisors())

    ## Cuspidal part
    cusp_dimension = 0

    tmp = ZZ(0)
    for t in ts:
        tmp += moebius(gcd(m // t, f)) * t
    tmp = tmp / len(ts)
    cusp_dimension += tmp * (2 * k - 3) / ZZ(12)
    print "1: ", cusp_dimension

    if m % 2 == 0:
        tmp = ZZ(0)
        for t in ts:
            tmp += moebius(gcd(m // t, f)) * kronecker_symbol(-4, t)
        tmp = tmp / len(ts)

        cusp_dimension += 1 / ZZ(2) * kronecker_symbol(8, 2 * k - 1) * tmp
        print "2: ", 1 / ZZ(2) * kronecker_symbol(8, 2 * k - 1) * tmp

    tmp = ZZ(0)
    for t in ts:
        tmp += moebius(gcd(m // t, f)) * kronecker_symbol(t, 3)
    tmp = tmp / len(ts)
    if m % 3 != 0:
        cusp_dimension += 1 / ZZ(3) * kronecker_symbol(k, 3) * tmp
        print ": ", 1 / ZZ(3) * kronecker_symbol(k, 3) * tmp
    elif k % 3 == 0:
        cusp_dimension += 2 / ZZ(3) * (-1)**k * tmp
        print "3: ", 2 / ZZ(3) * (-1)**k * tmp
    else:
        cusp_dimension += 1 / ZZ(3) * (kronecker_symbol(k, 3) +
                                       (-1)**(k - 1)) * tmp
        print "3: ", 1 / ZZ(3) * (kronecker_symbol(k, 3) + (-1)**(k - 1)) * tmp

    tmp = ZZ(0)
    for t in ts:
        tmp +=   moebius(gcd(m // t, f)) \
               * (t // t.squarefree_part()).isqrt() \
               * (2 if (m // t) % 4 == 0 else 1)
    tmp = tmp / len(ts)
    cusp_dimension -= 1 / ZZ(2) * tmp
    print "4: ", -1 / ZZ(2) * tmp

    tmp = ZZ(0)
    for t in ts:
        tmp +=   moebius(gcd(m // t, f)) \
               * sum(   (( len(BinaryQF_reduced_representatives(-d, True))
                           if d not in [3, 4] else ( 1 / ZZ(3) if d == 3 else 1 / ZZ(2) ))
                         if d % 4 == 0 or d % 4 == 3 else 0 )
                      * kronecker_symbol(-d, m // t)
                      * ( 1 if (m // t) % 2 != 0 else
                          ( 4 if (m // t) % 4 == 0 else 2 * kronecker_symbol(-d, 2) ))
                      for d in (4 * m).divisors() )
    tmp = tmp / len(ts)
    cusp_dimension -= 1 / ZZ(2) * tmp
    print "5: ", -1 / ZZ(2) * tmp

    if k == 2:
        cusp_dimension += len(
            (m // f // (m // f).squarefree_part()).isqrt().divisors())

    return eis_dimension + cusp_dimension
    def dynatomic_polynomial(self,period):
        r"""
        For a map `f:\mathbb{P}^1 \to \mathbb{P}^1` this function computes the dynatomic polynomial.
        The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `n`.

        ALGORITHM:

        For a positive integer `n`, let `[F_n,G_n]` be the coordinates of the `nth` iterate of `f`.
        Then construct

        .. MATH::

            \Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)}

        where `\mu` is the Moebius function.

        For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute

        .. MATH::

            \Phi^{\ast}_{m,n}(f)(x,y) = \Phi^{\ast}_n(f)(F_m,G_m)/\Phi^{\ast}_n(f)(F_{m-1},G_{m-1})

        REFERENCES:

        ..

        - B. Hutz. Efficient determination of rational preperiodic points for endomorphisms of projective space. arxiv:1210.6246, 2012.

        - P. Morton and P. Patel. The Galois theory of periodic points of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263.

        INPUT:

        - ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period

        OUTPUT:

        - If possible, a two variable polynomial in the coordinate ring of ``self``.
          Otherwise a fraction field element of the coordinate ring of ``self``

        .. TODO::

            Do the division when the base ring is p-adic or a function field so that the output is a polynomial.

        EXAMPLES::

            sage: P.<x,y>=ProjectiveSpace(QQ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+y^2,y^2])
            sage: f.dynatomic_polynomial(2)
            x^2 + x*y + 2*y^2

        ::

            sage: P.<x,y>=ProjectiveSpace(ZZ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+y^2,x*y])
            sage: f.dynatomic_polynomial(4)
            2*x^12 + 18*x^10*y^2 + 57*x^8*y^4 + 79*x^6*y^6 + 48*x^4*y^8 + 12*x^2*y^10 + y^12

        ::

            sage: P.<x,y>=ProjectiveSpace(CC,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+y^2,3*x*y])
            sage: f.dynatomic_polynomial(3)
            13.0000000000000*x^6 + 117.000000000000*x^4*y^2 +
            78.0000000000000*x^2*y^4 + y^6

        ::

            sage: P.<x,y>=ProjectiveSpace(QQ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2-10/9*y^2,y^2])
            sage: f.dynatomic_polynomial([2,1])
            x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6

        ::

            sage: P.<x,y>=ProjectiveSpace(QQ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2-29/16*y^2,y^2])
            sage: f.dynatomic_polynomial([2,3])
            x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 +
            8198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 +
            172692729/16777216*y^12

        ::

            sage: P.<x,y>=ProjectiveSpace(ZZ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2-y^2,y^2])
            sage: f.dynatomic_polynomial([1,2])
            x^2 - x*y

        ::

            sage: P.<x,y>=ProjectiveSpace(QQ,1)
            sage: H=Hom(P,P)
            sage: f=H([x^3-y^3,3*x*y^2])
            sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4)
            True

        ::

            sage: P.<x,y,z>=ProjectiveSpace(QQ,2)
            sage: H=Hom(P,P)
            sage: f=H([x^2+y^2,x*y,z^2])
            sage: f.dynatomic_polynomial(2)
            Traceback (most recent call last):
            ...
            TypeError: Does not make sense in dimension >1

        ::

            #TODO: it would be nice to get this to actually be a polynomial
            sage: P.<x,y>=ProjectiveSpace(Qp(5),1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+y^2,y^2])
            sage: f.dynatomic_polynomial(2)
            (x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y -
            x*y^2 + y^3)

        ::

            sage: L.<t>=PolynomialRing(QQ)
            sage: P.<x,y>=ProjectiveSpace(L,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+t*y^2,y^2])
            sage: f.dynatomic_polynomial(2)
            x^2 + x*y + (t + 1)*y^2

        ::
            sage: K.<c>=PolynomialRing(ZZ)
            sage: P.<x,y>=ProjectiveSpace(K,1)
            sage: H=Hom(P,P)
            sage: f=H([x^2+ c*y^2,y^2])
            sage: f.dynatomic_polynomial([1,2])
            x^2 - x*y + (c + 1)*y^2
       """
        if self.domain() != self.codomain():
            raise TypeError("Must have same domain and codomain to iterate")
        from sage.schemes.projective.projective_space import is_ProjectiveSpace
        if is_ProjectiveSpace(self.domain())==False:
            raise NotImplementedError("Not implemented for subschemes")
        if self.domain().dimension_relative()>1:
            raise TypeError("Does not make sense in dimension >1")

        if (isinstance(period,(list,tuple))==False):
            period=[0,period]
        try:
            period[0]=ZZ(period[0])
            period[1]=ZZ(period[1])
        except TypeError:
            raise TypeError("Period and preperiod must be integers")
        if period[1]<=0:
                raise AttributeError("Period must be at least 1")

        if period[0]!=0:
            m=period[0]
            fm=self.nth_iterate_map(m)
            fm1=self.nth_iterate_map(m-1)
            n=period[1]
            PHI=1;
            x=self.domain().gen(0)
            y=self.domain().gen(1)
            F=self._polys
            f=F
            for d in range(1,n+1):
                if n%d ==0:
                    PHI=PHI*((y*F[0]-x*F[1])**moebius(n/d))
                if d !=n: #avoid extra iteration
                    F=[f[0](F[0],F[1]),f[1](F[0],F[1])]
            if m!=0:
                PHI=PHI(fm._polys)/PHI(fm1._polys)
        else:
            PHI=1;
            x=self.domain().gen(0)
            y=self.domain().gen(1)
            F=self._polys
            f=F
            for d in range(1,period[1]+1):
                if period[1]%d ==0:
                    PHI=PHI*((y*F[0]-x*F[1])**moebius(period[1]/d))
                if d !=period[1]: #avoid extra iteration
                    F=[f[0](F[0],F[1]),f[1](F[0],F[1])]
        from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
        from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
        if self.domain().base_ring()==RealField() or self.domain().base_ring()==ComplexField():
            PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
        elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)):
            from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing
            from sage.rings.function_field.function_field import is_FunctionField
            BR=self.domain().base_ring().base_ring()
            if is_pAdicField(BR) or is_pAdicRing(BR) or is_FunctionField(BR):
                raise NotImplementedError("Not implemented")
            PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
            #do it again to divide out by denominators of coefficients
            PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
        if PHI.denominator()==1:
            PHI=self.coordinate_ring()(PHI)
        return(PHI)