Ticket #11422: trac_11422-sl2z_subgroups.patch

File trac_11422-sl2z_subgroups.patch, 110.8 KB (added by vdelecroix, 8 years ago)
  • sage/modular/arithgroup/arithgroup_generic.py

    # HG changeset patch
    # User Vincent Delecroix <20100.delecroix at gmail.com>
    # Date 1309989060 -7200
    # Node ID 35716f045476344467e11d745ca6b96deb5ef534
    # Parent  ebb9a83e8edb403cb03062e4f7976b2bd07d64d8
    trac 11422: Subgroup of the modular group SL(2,Z) as permutation groups
    
    This patch add many features to the class ArithemticSubgroup_Permutation.
      * redesign of the implementation: the group now stores the action of four
     generators of sl2z. Namely, an element of order 2 (named S2), an element of
     order 3 (named S3) and two parabolic elements (L and R).
      * creation of a class for odd subgroup (that does not contain minus
     identity)
      * computation of invariants that override the ones from the generic class
      * creation of a Test class
    
    diff -r ebb9a83e8edb -r 35716f045476 sage/modular/arithgroup/arithgroup_generic.py
    a b  
    9292
    9393    def coset_reps(self, G=None):
    9494        r"""
    95         Return coset representatives for self \\ G, where G is another
     95        Return right coset representatives for self \\ G, where G is another
    9696        arithmetic subgroup that contains self.  If G = None, default to G =
    9797        SL2Z.
    9898       
     
    117117        return self.todd_coxeter(G)[0]
    118118
    119119    @cached_method
    120     def todd_coxeter(self, G=None, limit = 100):
     120    def todd_coxeter(self, G=None, on_right=True):
    121121        r"""
    122         Compute coset representatives for self \\ G via Todd-Coxeter enumeration.
    123         If G = None, default to G = SL2Z. Also computes generators for G at the
    124         same time. Return value is a tuple (list of coset reps, list of
    125         generators).
     122        Compute coset representatives for self \\ G and action of standard
     123        generators on them via Todd-Coxeter enumeration.
     124
     125        If ``G`` is ``None``, default to ``SL2Z``. The method also computes
     126        generators of the subgroup at same time.
    126127       
     128        INPUT:
     129
     130        - ``G`` - intermediate subgroup (currently not implemented if diffferent
     131          from SL(2,Z))
     132
     133        - ``on_right`` - boolean (default: True) - if True return right coset
     134          enumeration, if False return left one.
     135
    127136        This is *extremely* slow in general.
    128137
    129         EXAMPLE:
     138        OUTPUT:
     139       
     140        - a list of coset representatives
     141       
     142        - a list of generators for the group
     143       
     144        - ``l`` - list of integers that correspond to the action of the
     145          standard parabolic element [[1,1],[0,1]] of `SL(2,\ZZ)` on the cosets
     146          of self.
     147         
     148        - ``s`` - list of integers that correspond to the action of the standard
     149          element of order `2` [[0,-1],[1,0]] on the cosets of self.
    130150
    131             sage: Gamma0(3).todd_coxeter()
    132             ([[1 0]
    133             [0 1], [ 0 -1]
    134             [ 1  0], [ 0 -1]
    135             [ 1  1], [ 0 -1]
    136             [ 1  2]], [[1 1]
    137             [0 1], [-1  0]
    138             [ 0 -1], [ 1  0]
    139             [-3  1], [-2 -1]
    140             [ 3  1], [-1 -1]
    141             [ 3  2]])
     151        EXAMPLES::
     152
     153            sage: L = SL2Z([1,1,0,1])
     154            sage: S = SL2Z([0,-1,1,0])
     155
     156            sage: G = Gamma(2)
     157            sage: reps, gens, l, s = G.todd_coxeter()
     158            sage: len(reps) == G.index()
     159            True
     160            sage: all(reps[i] * L * ~reps[l[i]] in G for i in xrange(6))
     161            True
     162            sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(6))
     163            True
     164
     165            sage: G = Gamma0(7)
     166            sage: reps, gens, l, s = G.todd_coxeter()
     167            sage: len(reps) == G.index()
     168            True
     169            sage: all(reps[i] * L * ~reps[l[i]] in G for i in xrange(8))
     170            True
     171            sage: all(reps[i] * S * ~reps[s[i]] in G for i in xrange(8))
     172            True
     173
     174            sage: G = Gamma1(3)
     175            sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
     176            sage: len(reps) == G.index()
     177            True
     178            sage: all(~reps[l[i]] * L * reps[i] in G for i in xrange(8))
     179            True
     180            sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(8))
     181            True
     182
     183            sage: G = Gamma0(5)
     184            sage: reps, gens, l, s = G.todd_coxeter(on_right=False)
     185            sage: len(reps) == G.index()
     186            True
     187            sage: all(~reps[l[i]] * L * reps[i] in G for i in xrange(6))
     188            True
     189            sage: all(~reps[s[i]] * S * reps[i] in G for i in xrange(6))
     190            True
    142191        """
    143 
    144192        from all import SL2Z
    145193
    146194        if G is None:
    147195            G = SL2Z
    148196        if G != SL2Z:
    149197            raise NotImplementedError, "Don't know how to compute coset reps for subgroups yet"
    150         s = SL2Z([1,1,0,1])
    151         t = SL2Z([0,-1,1,0])
    152198
    153         reps = [SL2Z(1)]
    154         schecked = []
    155         tchecked = []
     199        id = SL2Z([1,0,0,1])
     200        l = SL2Z([1,1,0,1])
     201        s = SL2Z([0,-1,1,0])
     202
     203        reps = [id]       # coset representatives
     204        reps_inv = {id:0} # coset representatives index
     205
     206        l_wait_back = [id] # rep with no incoming s_edge
     207        s_wait_back = [id] # rep with no incoming l_edge
     208        l_wait = [id]      # rep with no outgoing l_edge
     209        s_wait = [id]      # rep with no outgoing s_edge
     210
     211        l_edges = [None]    # edges for l
     212        s_edges = [None]    # edges for s
    156213
    157214        gens = []
    158215       
    159         for i in xrange(limit):
    160             found_new = 0
     216        while l_wait or s_wait:
     217            if l_wait:
     218                x = l_wait.pop(0)
     219                y = x
     220                not_end = True
     221                while not_end:
     222                    if on_right:
     223                        y = y*l
     224                    else:
     225                        y = l*y
     226                    for i in xrange(len(l_wait_back)):
     227                        v = l_wait_back[i]
     228                        if on_right:
     229                            yy = y*~v
     230                        else:
     231                            yy = ~v*y
     232                        if yy in self:
     233                            l_edges[reps_inv[x]] = reps_inv[v]
     234                            del l_wait_back[i]
     235                            if yy != id:
     236                                gens.append(self(yy))
     237                            not_end = False
     238                            break
     239                    else:
     240                        reps_inv[y] = len(reps)
     241                        l_edges[reps_inv[x]] = len(reps)
     242                        reps.append(y)
     243                        l_edges.append(None)
     244                        s_edges.append(None)
     245                        s_wait_back.append(y)
     246                        s_wait.append(y)
     247                    x = y
    161248
    162             for x in reps:
    163                
    164                 if not (x in schecked):
    165                     schecked.append(x)
    166                     y = x
    167                     end = 0
    168                     while end == 0:
     249            if s_wait:
     250                x = s_wait.pop(0)
     251                y = x
     252                not_end = True
     253                while not_end:
     254                    if on_right:
    169255                        y = y*s
    170                         yknown = False
    171                         for v in reps:
    172                             if y*(~v) in self:
    173                                 if y*(~v) != SL2Z(1):
    174                                     gens.append(y*(~v))
    175                                 end = 1
    176                                 break
    177                         if not end:
    178                             reps.append(y)
    179                             schecked.append(y)
    180                             found_new = 1
    181  
    182                 if not (x in tchecked):
    183                     tchecked.append(x)
    184                     y = x
    185                     end = 0
    186                     while end == 0:
    187                         y = y*t
    188                         yknown = False
    189                         for v in reps:
    190                             if y*(~v) in self:
    191                                 if y*(~v) != SL2Z(1):
    192                                     gens.append(y*(~v))
    193                                 end = 1
    194                                 break
    195                         if not end:
    196                             reps.append(y)
    197                             tchecked.append(y)
    198                             found_new = 1
     256                    else:
     257                        y = s*y
     258                    for i in xrange(len(s_wait_back)):
     259                        v = s_wait_back[i]
     260                        if on_right:
     261                            yy = y*~v
     262                        else:
     263                            yy = ~v*y
     264                        if yy in self:
     265                            s_edges[reps_inv[x]] = reps_inv[v]
     266                            del s_wait_back[i]
     267                            if yy != id:
     268                                gens.append(self(yy))
     269                            not_end = False
     270                            break
     271                    else:
     272                        reps_inv[y] = len(reps)
     273                        s_edges[reps_inv[x]] = len(reps)
     274                        reps.append(y)
     275                        l_edges.append(None)
     276                        s_edges.append(None)
     277                        l_wait_back.append(y)
     278                        l_wait.append(y)
     279                    x = y
    199280
    200             if found_new == 0:
    201                 return reps, gens
    202         raise Exception, "Todd-Coxeter enumeration did not terminate in %s steps" % limit
    203 
     281        return reps, gens, l_edges, s_edges
     282                   
    204283    def nu2(self):
    205284        r"""
    206285        Return the number of orbits of elliptic points of order 2 for this
     
    413492        """
    414493        return [-1, 0, 0, -1] in self
    415494
    416 
    417495    def order(self):
    418496        r"""
    419497        Return the number of elements in this arithmetic subgroup.
     
    732810
    733811        return ZZ(1 + (self.projective_index()) / ZZ(12)  - (self.nu2())/ZZ(4) - (self.nu3())/ZZ(3) - self.ncusps()/ZZ(2))
    734812
    735     @cached_method
    736813    def generators(self):
    737814        r"""
    738815        Return generators for this congruence subgroup.
     
    747824
    748825            sage: Gamma(2).generators()
    749826            [[1 2]
    750             [0 1],
    751             [-1  0]
    752             [ 0 -1],
    753             [-1  0]
    754             [ 0 -1],
    755             [ 1  0]
    756             [-2  1],
    757             [-1  2]
    758             [-2  3],
    759             [-1  0]
    760             [ 2 -1],
    761             [1 0]
     827            [0 1], [-1  0]
     828            [ 0 -1], [ 1  0]
     829            [-2  1], [-1  0]
     830            [ 0 -1], [-1  2]
     831            [-2  3], [-1  0]
     832            [ 2 -1], [1 0]
    762833            [2 1]]
    763 
    764834        """
    765835        return self.todd_coxeter()[1]
    766836
     
    9721042                    else:
    9731043                        raise NotImplementedError, "Computation of dimensions of weight 1 cusp forms spaces not implemented in general"
    9741044
    975 
    9761045    def dimension_eis(self, k=2):
    9771046        r"""
    9781047        Return the dimension of the space of weight k Eisenstein series for
     
    10121081            else: # k = 1
    10131082                return ZZ(self.nregcusps()/ ZZ(2))
    10141083
    1015 
    10161084    def as_permutation_group(self):
    10171085        r"""
    1018         Return a representation of this arithmetic subgroup in terms of the
    1019         permutation action of SL2Z on the cosets of G.
     1086        Return self as an arithmetic subgroup defined in terms of the
     1087        permutation action of `SL(2,\ZZ)` on its right cosets.
    10201088
    1021         At present this is only implemented for even subgroups.
     1089        This method uses Todd-coxeter enumeration (via the method todd_coxeter) which
     1090        can be extremly slow for arithmetic subgroup with relatively large index in
     1091        `SL(2,\ZZ)`.
    10221092
    1023         EXAMPLE::
     1093        EXAMPLES::
    10241094
    1025             sage: Gamma0(3).as_permutation_group()
    1026             Arithmetic subgroup corresponding to permutations L=(2,3,4), R=(1,3,4)
    1027 
     1095            sage: G = Gamma(3)
     1096            sage: P = G.as_permutation_group(); P
     1097            Arithmetic subgroup of index 24
     1098            sage: G.ncusps() == P.ncusps()
     1099            True
     1100            sage: G.nu2() == P.nu2()
     1101            True
     1102            sage: G.nu3() == P.nu3()
     1103            True
     1104            sage: G.an_element() in P
     1105            True
     1106            sage: P.an_element() in G
     1107            True
    10281108        """
    1029         if not self.is_even(): raise NotImplementedError, "Permutation form only implemented for subgroups containing -1 at present"
    1030         from arithgroup_perm import convert_to_permgroup
    1031         return convert_to_permgroup(self)
     1109        _,_,l_edges,s2_edges=self.todd_coxeter()
     1110        n = len(l_edges)
     1111        s3_edges = [None] * n
     1112        r_edges = [None] * n
     1113        for i in xrange(n):
     1114            ii = s2_edges[l_edges[i]]
     1115            s3_edges[ii] = i
     1116            r_edges[ii] = s2_edges[i]
     1117        if self.is_even():
     1118            from sage.modular.arithgroup.arithgroup_perm import EvenArithmeticSubgroup_Permutation
     1119            g=EvenArithmeticSubgroup_Permutation(S2=s2_edges,S3=s3_edges,L=l_edges,R=r_edges)
     1120        else:
     1121            from sage.modular.arithgroup.arithgroup_perm import OddArithmeticSubgroup_Permutation
     1122            g=OddArithmeticSubgroup_Permutation(S2=s2_edges,S3=s3_edges,L=l_edges,R=r_edges)
     1123        g.relabel()
     1124        return g
    10321125
    10331126    def sturm_bound(self, weight=2):
    10341127        r"""
  • sage/modular/arithgroup/arithgroup_perm.py

    diff -r ebb9a83e8edb -r 35716f045476 sage/modular/arithgroup/arithgroup_perm.py
    a b  
    11r"""
    2 Arithmetic subgroups defined by permutations
     2Arithmetic subgroups defined by permutation action of generators on cosets.
    33
    4 A theorem of Millington states that an arithmetic subgroup of index `N` is
    5 uniquely determined by two elements generating a transitive subgroup of the
    6 symmetric group `S_N` and satisfying a certain algebraic relation.
     4A subgroup of finite index `H` of a finitely generated group `G` is completely
     5described by the action of the generators on the right cosets `H\\G = \{Hg\}`.
     6After some arbitrary choice of numbering one can identify the action of
     7generators as elements of a symmetric group acting transitively (and satisfying
     8the relations of the relators in G). As `{\rm SL}_2(\ZZ)` is a central extension of a
     9free product of cyclic group one can easily design algorithms from this point of
     10view.
    711
    8 These functions are based on Chris Kurth's *KFarey* package.
     12The generators of `{\rm SL}_2(\ZZ)` used in this module are named as follows `s_2`,
     13`s_3`, `l`, `r` which are defined by
     14
     15.. MATH::
     16
     17    s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
     18    s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
     19    l = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix},\quad
     20    r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
     21
     22Those generators satisfy the following relations
     23
     24.. MATH::
     25
     26    s_2^2 = s_3^3 = -1 \quad r = s_2^{-1}\ l^{-1}\ s_2.
     27
     28In particular not all four are needed to generate the whole `{\rm SL}_2(\ZZ)`. Three
     29couples which which generate `{\rm SL}_2(\ZZ)` are of particular interest:
     30
     31- `(l,r)` as the pair is involved in the continued fraction algorithm,
     32- `(l,s_2)` similar as the one above because of the relations,
     33- `(s_2,s_3)` as the pair generates freely `{\rm PSL}_2(\ZZ)`.
     34
     35Part of these functions are based on Chris Kurth's *KFarey* package [Kur08]. For
     36tests see the file sage.modular.arithgroup.tests
     37
     38REFERENCES:
     39
     40.. [AtSD71] A. O. L. Atkin and H. P. F. Swinnerton-Dyer, "Modular forms on
     41   noncongruence subgroups", Proc. Symp. Pure Math., Combinatorics (T. S. Motzkin,
     42   ed.), vol. 19, AMS, Providence 1971
     43
     44.. [Hsu96] Tim Hsu, "Identifying congruence subgroups of the modular group",
     45   Proc. AMS 124, no. 5, 1351-1359 (1996)
     46
     47.. [Hsu97] Tim Hsu, "Permutation techniques for coset representations of modular
     48   subgroups", in L. Schneps (ed.), Geometric Galois Actions II: Dessins
     49   d'Enfants, Mapping Class Groups and Moduli, volume 243 of LMS Lect. Notes,
     50   67-77, Cambridge Univ. Press (1997)
     51
     52.. [Go09] Alexey G. Gorinov, "Combinatorics of double cosets and fundamental
     53   domains for the subgroups of the modular group", preprint
     54   http://fr.arxiv.org/abs/0901.1340
     55
     56.. [Kul91] Ravi Kulkarni "An arithmetic geometric method in the study of the
     57   subgroups of the modular group", American Journal of Mathematics 113 (1991),
     58   no 6, 1053-1133
     59
     60.. [Kur08] Chris Kurth, "K Farey package for Sage",
     61   http://www.public.iastate.edu/~kurthc/research/index.html
     62
     63.. [KuLo] Chris Kurth and Ling Long, "Computations with finite index subgroups
     64   of `{\rm PSL}_2(ZZ)` using Farey symbols"
     65
     66.. [Ve] Helena Verrill, "Fundamental domain drawer", java program,
     67   http://www.math.lsu.edu/~verrill/
     68
     69TODO:
     70
     71- modular Farey symbols
     72
     73- computation of generators of a modular subgroup with a standard surface
     74  group presentation. In other words, compute a presentation of the form
     75
     76  .. MATH::
     77
     78      \langle x_i,y_i,c_j |\ \prod_i [x_i,y_i] \prod_j c_j^{\nu_j} = 1\rangle
     79
     80  where the elements `x_i` and `y_i` are hyperbolic and `c_j` are parabolic
     81  (`\nu_j=\infty`) or elliptic elements (`\nu_j < \infty`).
     82
     83- computation of centralizer.
     84
     85- generation of modular (even) subgroups of fixed index.
    986
    1087AUTHORS:
    1188
     
    1390
    1491- David Loeffler (2009): adapted functions from KFarey for inclusion into Sage
    1592
     93- Vincent Delecroix (2010): implementation for odd groups, new design,
     94  improvements, documentation
    1695"""
    1796
    1897################################################################################
     
    29108
    30109from all import SL2Z
    31110from arithgroup_generic import ArithmeticSubgroup
    32 from sage.rings.all import Integers, ZZ
     111from arithgroup_element import ArithmeticSubgroupElement
     112from sage.rings.all import Zmod, ZZ
     113from sage.rings.infinity import Infinity
     114from sage.misc.cachefunc import cached_method
     115import sage.rings.arith as arith
    33116
    34 Lm = SL2Z([1,1,0,1])
    35 Rm = SL2Z([1,0,1,1])
     117from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
    36118
    37 class ArithmeticSubgroup_Permutation(ArithmeticSubgroup):
     119
     120Idm = SL2Z([1,0,0,1])    # identity
     121
     122Lm = SL2Z([1,1,0,1])     # parabolic that fixes infinity
     123Rm = SL2Z([1,0,1,1])     # parabolic that fixes 0
     124S2m = SL2Z([0,-1,1,0])   # elliptic of order 2 (fix i)
     125S3m = SL2Z([0,1,-1,1])   # elliptic of order 3 (fix j)
     126
     127S2mi = SL2Z([0,1,-1,0])  # the inverse of S2m in SL(2,Z)
     128S3mi = SL2Z([1,-1,1,0])  # the inverse of S3m in SL(2,Z)
     129Lmi = SL2Z([1,-1,0,1])   # the inverse of Lm in SL(2,Z)
     130Rmi = SL2Z([1,0,-1,1])   # the inverse of Rm in SL(2,Z)
     131
     132def sl2z_word_problem(A):
     133    r"""
     134    Given an element of `{\rm SL}_2(\ZZ)`, express it as a word in the generators L =
     135    [1,1,0,1] and R = [1,0,1,1].
     136
     137    The return format is a list of pairs ``(a,b)``, where ``a = 0`` or ``1``
     138    denoting ``L`` or ``R`` respectively, and ``b`` is an integer exponent.
     139
     140    See also the method eval_sl2z_word.
     141
     142    EXAMPLES::
     143
     144        sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word, sl2z_word_problem
     145        sage: m = SL2Z([1,0,0,1])
     146        sage: eval_sl2z_word(sl2z_word_problem(m)) == m
     147        True
     148        sage: m = SL2Z([0,-1,1,0])
     149        sage: eval_sl2z_word(sl2z_word_problem(m)) == m
     150        True
     151        sage: m = SL2Z([7,8,-50,-57])
     152        sage: eval_sl2z_word(sl2z_word_problem(m)) == m
     153        True
     154    """
     155    A = SL2Z(A)
     156    output=[]
     157
     158    ## If A00 is zero
     159    if A[0,0]==0:
     160        c=A[1,1]
     161        if c != 1:
     162            A=A*Lm**(c-1)*Rm*Lmi
     163            output.extend([(0,1-c),(1,-1),(0,1)])
     164        else:
     165            A=A*Rm*Lmi
     166            output.extend([(1,-1),(0,1)])
     167
     168    if A[0,0]<0:   # Make sure A00 is positive
     169        A=SL2Z(-1)*A
     170        output.extend([(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)])
     171
     172    if A[0,1]<0:   # if A01 is negative make it positive
     173        n=(-A[0,1]/A[0,0]).ceil()  #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]
     174        A=A*Lm**n
     175        output.append((0, -n))
     176   ## At this point A00>0 and A01>=0
     177    while not (A[0,0]==0 or A[0,1]==0):
     178        if A[0,0]>A[0,1]:
     179            n=(A[0,0]/A[0,1]).floor()
     180            A=A*SL2Z([1,0,-n,1])
     181            output.append((1, n))
     182
     183        else:      #A[0,0]<=A[0,1]
     184            n=(A[0,1]/A[0,0]).floor()
     185            A=A*SL2Z([1,-n,0,1])
     186            output.append((0, n))
     187
     188    if A==SL2Z(1):
     189        pass       #done, so don't add R^0
     190    elif A[0,0]==0:
     191        c=A[1,1]
     192        if c != 1:
     193            A=A*Lm**(c-1)*Rm*Lmi
     194            output.extend([(0,1-c),(1,-1),(0, 1)])
     195        else:
     196            A=A*Rm*Lmi
     197            output.extend([(1,-1),(0,1)])
     198    else:
     199        c=A[1,0]
     200        if c:
     201            A=A*Rm**(-c)
     202            output.append((1,c))
     203
     204    output.reverse()
     205    return output
     206
     207def eval_sl2z_word(w):
     208    r"""
     209    Given a word in the format output by sl2z_word_problem, convert it back
     210    into an element of `{\rm SL}_2(\ZZ)`.
     211
     212    EXAMPLES::
     213   
     214        sage: from sage.modular.arithgroup.arithgroup_perm import eval_sl2z_word
     215        sage: eval_sl2z_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])
     216        [ 66 -59]
     217        [ 47 -42]
     218    """
     219    from sage.all import prod
     220    mat = [Lm,Rm]
     221    w0 = Idm
     222    w1 = w
     223    return w0 * prod((mat[a[0]]**a[1] for a in w1), Idm)
     224
     225def word_of_perms(w, p1, p2):
     226    r"""
     227    Given a word `w` as a list of 2-tuples ``(index,power)`` and permutations
     228    ``p1`` and ``p2`` return the product of ``p1`` and ``p2`` that corresponds
     229    to ``w``.
     230   
     231    EXAMPLES::
     232
     233        sage: import sage.modular.arithgroup.arithgroup_perm as ap
     234        sage: S2 = SymmetricGroup(4)
     235        sage: p1 = S2('(1,2)(3,4)')
     236        sage: p2 = S2('(1,2,3,4)')
     237        sage: ap.word_of_perms([(1,1),(0,1)], p1, p2) ==  p2 * p1
     238        True
     239        sage: ap.word_of_perms([(0,1),(1,1)], p1, p2) == p1 * p2
     240        True
     241    """
     242    if not isinstance(p1,PermutationGroupElement):
     243        p1 = PermutationGroupElement(p1)
     244    if not isinstance(p2,PermutationGroupElement):
     245        p2 = PermutationGroupElement(p2)
     246
     247    G = p1.parent()
     248    if G != p2.parent(): # find a minimal parent
     249        G2 = p2.parent()
     250        if G.has_coerce_map_from(G2):
     251            p2 = G(p2)
     252        elif G2.has_coerce_map_from(G):
     253            G = G2
     254            p1 = G(p1)
     255        else:
     256            from sage.groups.perm_gps.all import PermutationGroup
     257            G = PermutationGroup([p1,p2])
     258            p1 = G(p1)
     259            p2 = G(p2)
     260
     261    M = G.identity()
     262    p = [p1, p2]
     263    m = [p1.order(),p2.order()]
     264
     265    for i,j in w:
     266        M *= p[i]**(j%m[i])
     267
     268    return M
     269
     270def equalize_perms(l):
     271    r"""
     272    Transform a list of lists into a list of lists with identical length. Each list
     273    ``p`` in the argument is completed with ``range(len(p),n)`` where ``n`` is
     274    the maximal length of the lists in ``l``.
     275
     276    EXAMPLES::
     277
     278        sage: from sage.modular.arithgroup.arithgroup_perm import equalize_perms
     279        sage: l = [[],[1,0],[3,0,1,2]]
     280        sage: equalize_perms(l)
     281        sage: l
     282        [[0, 1, 2, 3], [1, 0, 2, 3], [3, 0, 1, 2]]
     283    """
     284    n=max(map(len,l))
     285    if n ==0:
     286        n = 1
     287    for p in l:
     288        p.extend(xrange(len(p),n))
     289
     290
     291def ArithmeticSubgroup_Permutation(
     292        S2=None, S3=None, L=None, R=None,
     293        relabel=False,
     294        check=True):
     295    r"""
     296    Generate a subgroup of `{\rm SL}_2(\ZZ)` from the action of generators on its
     297    right cosets.
     298
     299    Return an arithmetic subgroup knowing the action, given by permutations, of
     300    at least two standard generators on the its cosets. The generators
     301    considered are the following matrices
     302
     303    .. math::
     304
     305        s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
     306        s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
     307        l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
     308        r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
     309
     310    INPUT:
     311
     312    - ``S2``, ``S3``, ``L``, ``R`` - permutations - action of matrices on the
     313      right cosets (each coset is identified to an element of `\{1,\dots,n\}`
     314      where `1` is reserved for the identity coset).
     315
     316    - ``relabel`` - boolean (default: False) - if True, renumber the cosets in a
     317      canonical way.
     318
     319    - ``check`` - boolean (default: True) - check that the input is valid (it
     320      may be time efficient but less safer to set it to False)
     321
     322    EXAMPLES::
     323
     324        sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
     325        sage: G
     326        Arithmetic subgroup with permutations of right cosets
     327         S2=(1,2)(3,4)
     328         S3=(1,2,3)
     329         L=(1,4,3)
     330         R=(2,4,3)
     331        sage: G.index()
     332        4
     333
     334        sage: ArithmeticSubgroup_Permutation()
     335        Arithmetic subgroup with permutations of right cosets
     336         S2=()
     337         S3=()
     338         L=()
     339         R=()
     340    """
     341    gens = filter(lambda x: x is not None, [S2,S3,L,R])
     342    if len(gens) == 0:
     343        S2 = S3 = L = R = ''
     344    elif len(gens) < 2:
     345        raise ValueError, "need at least two generators"
     346
     347    if check:
     348        from sage.groups.perm_gps.all import PermutationGroup
     349
     350        G = PermutationGroup(gens)
     351        if not G.is_transitive():
     352            raise ValueError, "the group is not transitive"
     353
     354    if S2 is not None:
     355        S2 = PermutationGroupElement(S2,check=check)
     356    if S3 is not None:
     357        S3 = PermutationGroupElement(S3,check=check)
     358    if L is not None:
     359        L = PermutationGroupElement(L,check=check)
     360    if R is not None:
     361        R = PermutationGroupElement(R,check=check)
     362
     363    if L is not None:
     364        if R is not None: # initialize from L,R
     365            if S2 is None:
     366                S2 = R * ~L * R
     367            if S3 is None:
     368                S3 = L * ~R
     369        elif S2 is not None: # initialize from L,S2
     370            if S3 is None:
     371                S3 =  ~S2 * ~L
     372            if R is None:
     373                R = ~S2 * ~L * S2
     374        elif S3 is not None: # initialize from L,S3
     375            if S2 is None:
     376                S2 = ~L * ~S3
     377            if R is None:
     378                R = S3 * ~L * ~S3
     379    elif R is not None:
     380        if S2 is not None: # initialize from R, S2
     381            if L is None:
     382                L = ~S2 * ~R * S2
     383            if S3 is None:
     384                S3 = R * ~S2
     385        elif S3 is not None: # initialize from R, S3
     386            if L is None:
     387                L = ~S3 * ~R * S3
     388            if S2 is None:
     389                S2 = ~S3 * R
     390    else: # intialize from S2, S3
     391        if L is None:
     392            L = ~S3 * ~S2
     393        if R is None:
     394            R = S3 * S2
     395
     396    if check and (L != ~S3 * ~S2 or R != S3 * S2):
     397        raise ValueError, "wrong relations between generators"
     398
     399    inv = S2*S2
     400    if check and (inv != S3*S3*S3):
     401        raise ValueError, "S2^2 does not equal to S3^3"
     402
     403    s2 = [i-1 for i in S2.list()]
     404    s3 = [i-1 for i in S3.list()]
     405    l = [i-1 for i in L.list()]
     406    r = [i-1 for i in R.list()]
     407    equalize_perms((s2,s3,l,r))
     408
     409    if inv.is_one(): # the group is even
     410        G = EvenArithmeticSubgroup_Permutation(s2,s3,l,r)
     411    else:
     412        if (not check) or (inv.order() == 2 and inv in G.center()): # the group is odd
     413            G = OddArithmeticSubgroup_Permutation(s2,s3,l,r)
     414        else:
     415             raise ValueError, "wrong relations between generators"
     416
     417    if relabel:
     418        G.relabel()
     419
     420    return G
     421
     422class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup):
     423    r"""
     424    A subgroup of `{\rm SL}_2(\ZZ)` defined by the action of generators on its
     425    coset graph.
     426
     427    The class stores the action of generators `s_2`,`s_3`,`l`,`r` on right cosets
     428    `Hg` of a finite index subgroup `H < {\rm SL}_2(\ZZ)`. In particular the action of
     429    `{\rm SL}_2(\ZZ)` on the cosets is on right.
     430
     431    .. MATH::
     432
     433        s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
     434        s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
     435        l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
     436        r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
     437
     438    TEST::
     439
     440        sage: s2 = PermutationGroupElement('(1,2)(3,4)(5,6)')
     441        sage: s3 = PermutationGroupElement('(1,3,5)(2,4,6)')
     442        sage: G = ArithmeticSubgroup_Permutation(S2=s2, S3=s3)
     443        sage: G.S2() == s2
     444        True
     445        sage: G.S3() == s3
     446        True
     447        sage: G == ArithmeticSubgroup_Permutation(L=G.L(), R=G.R())
     448        True
     449        sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S2=G.S2())
     450        True
     451        sage: G == ArithmeticSubgroup_Permutation(L=G.L(), S3=G.S3())
     452        True
     453        sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S2=G.S2())
     454        True
     455        sage: G == ArithmeticSubgroup_Permutation(R=G.R(), S3=G.S3())
     456        True
     457        sage: G == ArithmeticSubgroup_Permutation(S2=G.S2(), S3=G.S3())
     458        True
     459
     460        sage: G = ArithmeticSubgroup_Permutation(S2='',S3='')
     461        sage: G == loads(dumps(G))
     462        True
     463
     464        sage: S2 = '(1,2)(3,4)(5,6)'
     465        sage: S3 = '(1,2,3)(4,5,6)'
     466        sage: G = ArithmeticSubgroup_Permutation(S2=S2, S3=S3)
     467        sage: loads(dumps(G)) == G
     468        True
     469    """
     470    def __eq__(self, other):
     471        r"""
     472        Equality test.
     473
     474        TESTS::
     475
     476            sage: G2 = Gamma(2)
     477            sage: G3 = Gamma(3)
     478            sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)")
     479            sage: (G2 == H) and (H == G2)
     480            True
     481            sage: (G3 == H) or (H == G3)
     482            False
     483
     484            sage: G2 = Gamma1(2)
     485            sage: G3 = Gamma1(3)
     486            sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
     487            sage: (G2 == H) or (H == G2)
     488            False
     489            sage: (G3 == H) and (H == G3)
     490            True
     491        """
     492        if isinstance(other, ArithmeticSubgroup_Permutation_class):
     493            return (self.is_odd() == other.is_odd() and
     494                    self.index() == other.index() and
     495                    self.relabel(inplace=False)._S2 == other.relabel(inplace=False)._S2 and
     496                    self.relabel(inplace=False)._S3 == other.relabel(inplace=False)._S3)
     497
     498        elif isinstance(other, ArithmeticSubgroup):
     499            return self == other.as_permutation_group()
     500
     501        else:
     502            raise NotImplemented
     503
     504    def _repr_(self):
     505        r"""
     506        String representation of self.
     507
     508        EXAMPLES::
     509
     510            sage: G = Gamma(2).as_permutation_group()
     511            sage: repr(G) #indirect doctest
     512            'Arithmetic subgroup with permutations of right cosets\n S2=(1,4)(2,6)(3,5)\n S3=(1,2,3)(4,5,6)\n L=(1,5)(2,4)(3,6)\n R=(1,6)(2,5)(3,4)'
     513            sage: G = Gamma(3).as_permutation_group()
     514            sage: repr(G) #indirect doctest
     515            'Arithmetic subgroup of index 24'
     516        """
     517        if self.index() < 20:
     518            return "Arithmetic subgroup with permutations of right cosets\n S2=%s\n S3=%s\n L=%s\n R=%s" %(
     519                self.S2(), self.S3(), self.L(), self.R())
     520
     521        else:
     522            return "Arithmetic subgroup of index %d" %self.index()
     523
     524    #
     525    # Attribute access
     526    #
     527
     528    def S2(self):
     529        r"""
     530        Return the action of the matrix `s_2` as a permutation of cosets.
     531
     532        .. MATH::
     533
     534            s_2 = \begin{pmatrix}0&-1\\1&0\end{pmatrix}
     535
     536        EXAMPLES::
     537
     538            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     539            sage: G.S2()
     540            (1,2)
     541        """
     542        return PermutationGroupElement([i+1 for i in self._S2], check=False)
     543
     544    def S3(self):
     545        r"""
     546        Return the action of the matrix `s_3` as a permutation of cosets.
     547
     548        .. MATH::
     549
     550           s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
     551
     552        EXAMPLES::
     553
     554            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     555            sage: G.S3()
     556            (1,2,3)
     557        """
     558
     559        return PermutationGroupElement([i+1 for i in self._S3], check=False)
     560
     561    def L(self):
     562        r"""
     563        Return the action of the matrix `l` as a permutation of cosets.
     564
     565        .. MATH::
     566
     567            l = \begin{pmatrix}1&1\\0&1\end{pmatrix}
     568
     569        EXAMPLES::
     570
     571            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     572            sage: G.L()
     573            (1,3)
     574        """
     575        return PermutationGroupElement([i+1 for i in self._L], check=False)
     576
     577    def R(self):
     578        r"""
     579        Return the action of the matrix `r` as a permutation of cosets.
     580
     581        .. MATH::
     582
     583            r = \begin{pmatrix}1&0\\1&1\end{pmatrix}
     584
     585        EXAMPLES::
     586
     587            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     588            sage: G.R()
     589            (2,3)
     590        """
     591        return PermutationGroupElement([i+1 for i in self._R], check=False)
     592
     593    def perm_group(self):
     594        r"""
     595        Return the underlying permutation group.
     596
     597        The permutation group returned is isomorphic to the action of the
     598        generators `s_2` (element of order two), `s_3` (element of order 3), `l`
     599        (parabolic element) and `r` (parabolic element) on right cosets (the
     600        action is on the right).
     601
     602        EXAMPLE::
     603           
     604            sage: import sage.modular.arithgroup.arithgroup_perm as ap
     605            sage: ap.HsuExample10().perm_group()
     606            Permutation Group with generators [(1,2)(3,4)(5,6)(7,8)(9,10), (1,4)(2,5,9,10,8)(3,7,6), (1,7,9,10,6)(2,3)(4,5,8), (1,8,3)(2,4,6)(5,7,10)]
     607        """
     608        from sage.groups.perm_gps.all import PermutationGroup
     609        return PermutationGroup([self.S2(), self.S3(), self.L(), self.R()])
     610
     611    def index(self):
     612        r"""
     613        Returns the index of this modular subgroup in the full modular group.
     614
     615        EXAMPLES::
     616
     617            sage: G = Gamma(2)
     618            sage: P = G.as_permutation_group()
     619            sage: P.index()
     620            6
     621            sage: G.index() == P.index()
     622            True
     623
     624            sage: G = Gamma0(8)
     625            sage: P = G.as_permutation_group()
     626            sage: P.index()
     627            12
     628            sage: G.index() == P.index()
     629            True
     630
     631            sage: G = Gamma1(6)
     632            sage: P = G.as_permutation_group()
     633            sage: P.index()
     634            24
     635            sage: G.index() == P.index()
     636            True
     637        """
     638        return len(self._S2)
     639
     640    #
     641    # Canonical renumbering
     642    #
     643
     644    def relabel(self, inplace=True):
     645        r"""
     646        Relabel the cosets of this modular subgroup in a canonical way.
     647
     648        The implementation of modular subgroup by action of generators on cosets
     649        depends on the choice of a numbering. This function provides
     650        canonical labels in the sense that two equal subgroups whith different
     651        labels are relabeled the same way. The default implementation relabels
     652        the group itself. If you want to get a relabeled copy of your modular
     653        subgroup, put to ``False`` the option ``inplace``.
     654
     655        ALGORITHM:
     656
     657        We give an overview of how the canonical labels for the modular subgroup
     658        are built. The procedure only uses the permutations `S3` and `S2` that
     659        define the modular subgroup and can be used to renumber any
     660        transitive action of the symmetric group. In other words, the algorithm
     661        construct a canonical representative of a transitive subgroup in its
     662        conjugacy class in the full symmetric group.
     663
     664        1. The identity is still numbered `0` and set the curent vertex to be
     665        the identity.
     666
     667        2. Number the cycle of `S3` the current vertex belongs to: if the
     668        current vertex is labeled `n` then the numbering in such way that the
     669        cycle becomes `(n, n+1, \ldots, n+k)`).
     670       
     671        3. Find a new current vertex using the permutation `S2`.
     672        If all vertices are relabeled then it's done, otherwise go to step 2.
     673
     674        EXAMPLES::
     675
     676            sage: S2 = "(1,2)(3,4)(5,6)"; S3 = "(1,2,3)(4,5,6)"
     677            sage: G1 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G1
     678            Arithmetic subgroup with permutations of right cosets
     679             S2=(1,2)(3,4)(5,6)
     680             S3=(1,2,3)(4,5,6)
     681             L=(1,4,5,3)
     682             R=(2,4,6,3)
     683            sage: G1.relabel(); G1   
     684            Arithmetic subgroup with permutations of right cosets
     685             S2=(1,2)(3,4)(5,6)
     686             S3=(1,2,3)(4,5,6)
     687             L=(1,4,5,3)
     688             R=(2,4,6,3)
     689
     690            sage: S2 = "(1,2)(3,5)(4,6)"; S3 = "(1,2,3)(4,5,6)"
     691            sage: G2 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G2
     692            Arithmetic subgroup with permutations of right cosets
     693             S2=(1,2)(3,5)(4,6)
     694             S3=(1,2,3)(4,5,6)
     695             L=(1,5,6,3)
     696             R=(2,5,4,3)
     697            sage: G2.relabel(); G2
     698            Arithmetic subgroup with permutations of right cosets
     699             S2=(1,2)(3,4)(5,6)
     700             S3=(1,2,3)(4,5,6)
     701             L=(1,4,5,3)
     702             R=(2,4,6,3)
     703
     704            sage: S2 = "(1,2)(3,6)(4,5)"; S3 = "(1,2,3)(4,5,6)"
     705            sage: G3 = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G3
     706            Arithmetic subgroup with permutations of right cosets
     707             S2=(1,2)(3,6)(4,5)
     708             S3=(1,2,3)(4,5,6)
     709             L=(1,6,4,3)
     710             R=(2,6,5,3)     
     711            sage: G4 = G3.relabel(inplace=False)
     712            sage: G4
     713            Arithmetic subgroup with permutations of right cosets
     714             S2=(1,2)(3,4)(5,6)
     715             S3=(1,2,3)(4,5,6)
     716             L=(1,4,5,3)
     717             R=(2,4,6,3)
     718            sage: G3 is G4
     719            False
     720           
     721        TESTS::
     722
     723            sage: S2 = "(1,2)(3,6)(4,5)"
     724            sage: S3 = "(1,2,3)(4,5,6)"
     725            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     726            sage: H = G.relabel(inplace=False)
     727            sage: G is H
     728            False
     729            sage: G._S2 is H._S2 or G._S3 is H._S3 or G._L is H._L or G._R is H._R
     730            False
     731            sage: G.relabel(inplace=False) is H
     732            True
     733            sage: H.relabel(inplace=False) is H
     734            True
     735            sage: G.relabel(); G
     736            Arithmetic subgroup with permutations of right cosets
     737             S2=(1,2)(3,4)(5,6)
     738             S3=(1,2,3)(4,5,6)
     739             L=(1,4,5,3)
     740             R=(2,4,6,3)
     741            sage: G.relabel(inplace=False) is G
     742            True
     743        """
     744        if hasattr(self,'_canonical_label_group'):
     745            if inplace:
     746                if not (self is self._canonical_label_group):
     747                    self.__dict__ = self._canonical_label_group.__dict__
     748                    self._canonical_label_group = self
     749            else:
     750                return self._canonical_label_group
     751
     752        if inplace:
     753            G = self
     754        else:
     755            from copy import deepcopy
     756            G = deepcopy(self)
     757
     758        n = G.index()
     759        mapping = G._canonical_rooted_labels()
     760        S2 = G._S2
     761        S3 = G._S3
     762        L = G._L
     763        R = G._R
     764        G._S2 = [None]*n
     765        G._S3 = [None]*n
     766        G._L = [None]*n
     767        G._R = [None]*n
     768
     769        for i in xrange(n):
     770            G._S2[mapping[i]] = mapping[S2[i]]
     771            G._S3[mapping[i]] = mapping[S3[i]]
     772            G._L[mapping[i]] = mapping[L[i]]
     773            G._R[mapping[i]] = mapping[R[i]]
     774
     775        self._canonical_label_group = G
     776        G._canonical_label_group = G
     777
     778        if not inplace:
     779            return G
     780
     781    def _canonical_unrooted_labels(self):
     782        r"""
     783        Returns the smallest label among rooted label
     784
     785        OUTPUT:
     786
     787        A 3-tuple of lists corresponding to permutations. The first list is the
     788        mapping that gives the canonical labels and the second and third one
     789        correspond to the permutations obtained by the conjugation of ``S2`` and
     790        ``S3``.
     791
     792        EXAMPLES::
     793
     794            sage: S2 = "(1,2)(4,5)"
     795            sage: S3 = "(1,3,4)(2,5,6)"
     796            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     797            sage: s2,s3 = G._S2,G._S3
     798            sage: m,ss2,ss3 = G._canonical_unrooted_labels()
     799            sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
     800            True
     801            sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
     802            True
     803
     804            sage: S2 = "(1,2)(3,4)(5,6)"
     805            sage: S3 = "(1,3,4)(2,5,6)"
     806            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     807            sage: s2,s3 = G._S2,G._S3
     808            sage: m,ss2,ss3 = G._canonical_unrooted_labels()
     809            sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
     810            True
     811            sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
     812            True
     813
     814            sage: S2 = "(1,2)(3,4)(5,6)"
     815            sage: S3 = "(1,3,5)(2,4,6)"
     816            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     817            sage: s2,s3 = G._S2,G._S3
     818            sage: m,ss2,ss3 = G._canonical_unrooted_labels()
     819            sage: all(ss2[m[i]] == m[s2[i]] for i in xrange(6))
     820            True
     821            sage: all(ss3[m[i]] == m[s3[i]] for i in xrange(6))
     822            True
     823        """
     824        n = self.index()
     825        S2_win = [None]*n;  S3_win = [None]*n
     826        S2_test = [None]*n; S3_test = [None]*n
     827
     828        m_win = self._canonical_rooted_labels(0)
     829        for i in xrange(n): # conjugation
     830            S2_win[m_win[i]] = m_win[self._S2[i]]
     831            S3_win[m_win[i]] = m_win[self._S3[i]]
     832
     833        for j0 in xrange(1,self.index()):
     834            m_test = self._canonical_rooted_labels(j0)
     835            for i in xrange(n):
     836                S2_test[m_test[i]] = m_test[self._S2[i]]
     837                S3_test[m_test[i]] = m_test[self._S3[i]]
     838
     839            for i in xrange(n-1):
     840                if (S2_test[i] < S2_win[i] or
     841                    (S2_test[i] == S2_win[i] and S3_test[i] < S3_win[i])):
     842                    S2_win,S2_test = S2_test,S2_win
     843                    S3_win,S3_test = S3_test,S3_win
     844                    m_win = m_test
     845                    break
     846
     847        return m_win, S2_win, S3_win
     848
     849    def _canonical_rooted_labels(self, j0=0):
     850        r"""
     851        Return the permutation which correspond to the renumbering of self in
     852        order to get canonical labels.
     853
     854        If ``j0`` is 0 then the renumbering corresponds to the same group. If
     855        not, the renumbering corresponds to the conjugated subgroup such that
     856        ``j0`` becomes the identity coset.
     857       
     858        EXAMPLES::
     859
     860            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     861            sage: G._canonical_rooted_labels(0)
     862            [0, 1, 2]
     863            sage: G._canonical_rooted_labels(1)
     864            [2, 0, 1]
     865            sage: G._canonical_rooted_labels(2)
     866            [1, 2, 0]
     867        """
     868        x = self._S3
     869        y = self._S2
     870        n = len(x)
     871
     872        k = 0
     873        seen = [True] * n
     874        mapping = [None] * n
     875        waiting = []
     876
     877        while True:
     878            # intialize at j0
     879            mapping[j0] = k
     880            waiting.append(j0)
     881            k += 1
     882            # complete x cycle from j0
     883            j = x[j0]
     884            while j != j0:
     885                mapping[j] = k
     886                waiting.append(j)
     887                k += 1
     888                j = x[j]
     889
     890            # if everybody is labelled do not go further
     891            if k == n: break
     892
     893            # find another guy with y
     894            j0 = y[waiting.pop(0)]
     895            while mapping[j0] is not None:
     896                j0 = y[waiting.pop(0)]
     897
     898        return mapping
     899
     900    #
     901    # Contains and random element
     902    #
     903
     904    @cached_method
     905    def _index_to_lr_cusp_width(self):
     906        r"""
     907        Precomputation of cusps data of self for this modular subgroup.
     908       
     909        This is a central precomputation for the ``.__contains__()`` method and
     910        consists in two lists  of positive integers ``lc`` and ``rc`` of length
     911        the index of the subgroup. They are defined as follows: the number
     912        ``lc[i]`` (resp ``rc[i]``) is the lenth of the cycle of ``L`` (resp.
     913        ``R``) which contains ``i``.
     914
     915        EXAMPLES::
     916
     917            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="(1,2,3)")
     918            sage: G.L()
     919            (1,3)
     920            sage: G.R()
     921            (2,3)
     922            sage: G._index_to_lr_cusp_width()
     923            ([2, 1, 2], [1, 2, 2])
     924        """
     925        G = self.relabel(inplace=False)
     926
     927        l = G.L()
     928        l_cycle_length = [None]*self.index()
     929        for c in l.cycle_tuples(singletons=True):
     930            for i in c:
     931                l_cycle_length[i-1]=len(c)
     932
     933        r = G.R()
     934        r_cycle_length = [None]*self.index()
     935        for c in r.cycle_tuples(singletons=True):
     936            for i in c:
     937                r_cycle_length[i-1]=len(c)
     938
     939        return (l_cycle_length, r_cycle_length)
     940
     941    def __contains__(self, x):
     942        r"""
     943        Test whether ``x`` is in the group or not.
     944
     945        ALGORITHM:
     946
     947        An element of `{\rm SL}_2(\ZZ)` is in a given modular subgroup if it does not
     948        permute the identity coset!
     949
     950        TEST::
     951
     952            sage: G = Gamma(4)
     953            sage: m1 = G([1,4,0,1])
     954            sage: m2 = G([17,4,4,1])
     955            sage: m3 = G([1,-4,-4,17])
     956            sage: m4 = SL2Z([1,2,0,1])
     957            sage: P = G.as_permutation_group()
     958            sage: m1 in P
     959            True
     960            sage: m2 in P
     961            True
     962            sage: m3 in P
     963            True
     964            sage: m4 in P
     965            False
     966        """
     967        if x.parent() is self or x.parent() == self: return True
     968        if x not in SL2Z: return False
     969       
     970        w = sl2z_word_problem(x)
     971
     972        perms = [self.relabel(inplace=False)._L,self.relabel(inplace=False)._R]
     973        widths = self._index_to_lr_cusp_width()
     974
     975        k = 0
     976        for (i,j) in w:
     977            for _ in xrange(j % widths[i][k]):
     978                k = perms[i][k]
     979       
     980        return not k
     981
     982    def random_element(self, initial_steps=30):
     983        r"""
     984        Returns a random element in this subgroup.
     985       
     986        The algorithm uses a random walk on the Cayley graph of `{\rm SL}_2(\ZZ)` stopped
     987        at the first time it reaches the subgroup after at least
     988        ``initial_steps`` steps.
     989
     990        INPUT:
     991
     992        - ``initial_steps`` - positive integer (default: 30)
     993
     994        EXAMPLES::
     995
     996            sage: G = ArithmeticSubgroup_Permutation(S2='(1,3)(4,5)',S3='(1,2,5)(3,4,6)')
     997            sage: all(G.random_element() in G for _ in xrange(10))
     998            True
     999        """
     1000        from sage.misc.prandom import randint
     1001
     1002        i = 0
     1003        m = SL2Z(1)
     1004        for _ in xrange(initial_steps):
     1005            j = randint(0,1)
     1006            if j == 0:
     1007                i = self._S2[i]
     1008                m = m*S2m
     1009            else:
     1010                i = self._S3[i]
     1011                m = m*S3m
     1012
     1013        while i != 0:
     1014            j = randint(0,1)
     1015            if j == 0:
     1016                i = self._S2[i]
     1017                m = m*S2m
     1018            else:
     1019                i = self._S3[i]
     1020                m = m*S3m
     1021
     1022        return m
     1023
     1024    def permutation_action(self, x):
     1025        r"""
     1026        Given an element ``x`` of `{\rm SL}_2(\ZZ)`, compute the permutation of the
     1027        cosets of self given by right multiplication by ``x``.
     1028       
     1029        EXAMPLE::
     1030
     1031            sage: import sage.modular.arithgroup.arithgroup_perm as ap
     1032            sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))
     1033            (1,4,6,2,10,5,3,7,8,9)
     1034        """
     1035        return word_of_perms(sl2z_word_problem(x), self.L(), self.R())
     1036
     1037    def __call__(self, g, check=True):
     1038        r"""
     1039        Create an element of this group from ``g``. If check=True (the default),
     1040        perform the (possibly rather computationally-intensive) check to make
     1041        sure ``g`` is in this group.
     1042
     1043        EXAMPLE::
     1044           
     1045            sage: import sage.modular.arithgroup.arithgroup_perm as ap
     1046            sage: m = SL2Z([1,1,0,1])
     1047            sage: m in ap.HsuExample10()
     1048            False
     1049            sage: ap.HsuExample10()(m)
     1050            Traceback (most recent call last):
     1051            ...
     1052            TypeError: The element is not in group
     1053            sage: ap.HsuExample10()(m, check=False)
     1054            [1 1]
     1055            [0 1]
     1056        """
     1057        g = SL2Z(g)
     1058        if not check or g in self:
     1059            return ArithmeticSubgroupElement(parent=self,x=g,check=check)
     1060        raise TypeError, "The element is not in group"
     1061
     1062    #
     1063    # Group stuff
     1064    #
     1065
     1066    def is_normal(self):
     1067        r"""
     1068        Test whether the group is normal
     1069
     1070        EXAMPLES::
     1071
     1072            sage: G = Gamma(2).as_permutation_group()
     1073            sage: G.is_normal()
     1074            True
     1075
     1076            sage: G = Gamma1(2).as_permutation_group()
     1077            sage: G.is_normal()
     1078            False
     1079        """
     1080        N = self.index()
     1081        G = self.relabel(inplace=False)
     1082        s2 = G._S2
     1083        s3 = G._S3
     1084        ss2 = [None]*N
     1085        ss3 = [None]*N
     1086        for j in [s2[0],s3[0]]:
     1087            m = G._canonical_rooted_labels(j)
     1088            for i in xrange(N):
     1089                ss2[m[i]] = m[s2[i]]
     1090                ss3[m[i]] = m[s3[i]]
     1091            if s2 != ss2 or s3 != ss3:
     1092                return False
     1093        return True
     1094
     1095    def _conjugate(self,j0):
     1096        r"""
     1097        Return the conjugate of self rooted at j0.
     1098
     1099        EXAMPLES::
     1100
     1101            sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)(4,5,6)')
     1102            sage: G
     1103            Arithmetic subgroup with permutations of right cosets
     1104             S2=(1,2)(3,4)
     1105             S3=(1,2,3)(4,5,6)
     1106             L=(1,4,6,5,3)
     1107             R=(2,4,5,6,3)
     1108            sage: G._conjugate(0) == G
     1109            True
     1110            sage: G._conjugate(4)
     1111            Arithmetic subgroup with permutations of right cosets
     1112             S2=(3,4)(5,6)
     1113             S3=(1,2,3)(4,5,6)
     1114             L=(1,4,5,3,2)
     1115             R=(1,2,4,6,3)
     1116        """
     1117        N = self.index()
     1118        s2 = self._S2
     1119        s3 = self._S3
     1120        l = self._L
     1121        r = self._R
     1122        ss2 = [None]*N
     1123        ss3 = [None]*N
     1124        ll = [None]*N
     1125        rr = [None]*N
     1126
     1127        m = self._canonical_rooted_labels(j0)
     1128        for i in xrange(N):
     1129            ss2[m[i]] = m[s2[i]]
     1130            ss3[m[i]] = m[s3[i]]
     1131            ll[m[i]] = m[l[i]]
     1132            rr[m[i]] = m[r[i]]
     1133        return self.__class__(ss2,ss3,ll,rr,True)
     1134
     1135    def coset_graph(self,
     1136            right_cosets=False,
     1137            s2_edges=True, s3_edges=True, l_edges=False, r_edges=False,
     1138            s2_label='s2', s3_label='s3', l_label='l', r_label='r'):
     1139        r"""
     1140        Return the right (or left) coset graph.
     1141
     1142        INPUT:
     1143
     1144        - ``right_cosets`` - bool (default: False) - right or left coset graph
     1145
     1146        - ``s2_edges`` - bool (default: True) - put edges associated to s2
     1147
     1148        - ``s3_edges`` - bool (default: True) - put edges associated to s3
     1149
     1150        - ``l_edges`` - bool (default: False) - put edges associated to l
     1151
     1152        - ``r_edges`` - bool (default: False) - put edges associated to r
     1153
     1154        - ``s2_label``, ``s3_label``, ``l_label``, ``r_label`` - the labels to
     1155          put on the edges corresponding to the generators action. Use ``None``
     1156          for no label.
     1157
     1158        EXAMPLES::
     1159       
     1160            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)",S3="()")
     1161            sage: G
     1162            Arithmetic subgroup with permutations of right cosets
     1163             S2=(1,2)
     1164             S3=()
     1165             L=(1,2)
     1166             R=(1,2)
     1167            sage: G.index()
     1168            2
     1169            sage: G.coset_graph()
     1170            Looped multi-digraph on 2 vertices
     1171        """
     1172        from sage.graphs.digraph import DiGraph
     1173        res = DiGraph(multiedges=True,loops=True)
     1174        res.add_vertices(range(self.index()))
     1175
     1176
     1177        if right_cosets: # invert the permutations
     1178            S2 = [None]*self.index()
     1179            S3 = [None]*self.index()
     1180            L = [None]*self.index()
     1181            R = [None]*self.index()
     1182            for i in xrange(self.index()):
     1183                S2[self._S2[i]] = i
     1184                S3[self._S3[i]] = i
     1185                L[self._L[i]] = i
     1186                R[self._R[i]] = i
     1187
     1188        else:
     1189            S2 = self._S2
     1190            S3 = self._S3
     1191            L = self._L
     1192            R = self._R
     1193
     1194        if s2_edges:
     1195            if s2_label is not None:
     1196               res.add_edges((i,S2[i],s2_label) for i in xrange(self.index()))
     1197            else:
     1198                res.add_edges((i,S2[i]) for i in xrange(self.index()))
     1199
     1200        if s3_edges:
     1201            if s3_label is not None:
     1202                res.add_edges((i,S3[i],s3_label) for i in xrange(self.index()))
     1203            else:
     1204                res.add_edges((i,S3) for i in xrange(self.index()))
     1205
     1206        if l_edges:
     1207            if l_label is not None:
     1208                res.add_edges((i,L[i],l_label) for i in xrange(self.index()))
     1209            else:
     1210                res.add_edges((i,L[i]) for i in xrange(self.index()))
     1211
     1212        if r_edges:
     1213            if r_label is not None:
     1214                res.add_edges((i,R[i],r_label) for i in xrange(self.index()))
     1215            else:
     1216                res.add_edges((i,R[i]) for i in xrange(self.index()))
     1217
     1218        res.plot.options['color_by_label'] = True
     1219
     1220        if s2_label or s3_label or l_label or r_label:
     1221            res.plot.options['edge_labels'] = True
     1222
     1223        return res
     1224     
     1225    def generalised_level(self):
     1226        r"""
     1227        Return the generalised level of this subgroup.
     1228
     1229        The *generalised level* of a subgroup of the modular group is the least
     1230        common multiple of the widths of the cusps. It was proven by Wohlfart
     1231        that if the subgroup is a congruence subgroup then the (conventional)
     1232        level coincide with the generalised level.
     1233
     1234        EXAMPLES::
     1235
     1236            sage: G = Gamma(2).as_permutation_group()
     1237            sage: G.generalised_level()
     1238            2
     1239            sage: G = Gamma0(3).as_permutation_group()
     1240            sage: G.generalised_level()
     1241            3
     1242        """
     1243        return arith.lcm(self.cusp_widths())
     1244
     1245class OddArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
     1246    def __init__(self, S2, S3, L, R, canonical_labels=False):
     1247        r"""
     1248        TESTS::
     1249
     1250            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
     1251            sage: G
     1252            Arithmetic subgroup with permutations of right cosets
     1253             S2=(1,2,3,4)
     1254             S3=(1,3)(2,4)
     1255             L=(1,2,3,4)
     1256             R=(1,4,3,2)
     1257            sage: loads(dumps(G)) == G
     1258            True
     1259        """
     1260        self._S2 = S2
     1261        self._S3 = S3
     1262        self._L = L
     1263        self._R = R
     1264        if canonical_labels:
     1265            self._canonical_label_group = self
     1266
     1267    def __reduce__(self):
     1268        r"""
     1269        Return the data used to construct self. Used in pickling.
     1270
     1271        TESTS::
     1272
     1273            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
     1274            sage: G == loads(dumps(G))  #indirect doctest
     1275            True
     1276            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)",relabel=True)
     1277            sage: GG = loads(dumps(G))
     1278            sage: GG == G #indirect doctest
     1279            True
     1280            sage: GG.relabel(inplace=False) is GG
     1281            True
     1282        """
     1283        if hasattr(self,'_canonical_label_group'):
     1284            canonical_labels = (self is self._canonical_label_group)
     1285        else:
     1286            canonical_labels = False
     1287        return (OddArithmeticSubgroup_Permutation,
     1288                (self._S2,self._S3,self._L,self._R,canonical_labels))
     1289
     1290    def is_odd(self):
     1291        r"""
     1292        Test whether the group is odd.
     1293
     1294        EXAMPLES::
     1295
     1296            sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
     1297            sage: G.is_odd()
     1298            True
     1299        """
     1300        return True
     1301
     1302    def is_even(self):
     1303        r"""
     1304        Test whether the group is even.
     1305       
     1306        EXAMPLES::
     1307
     1308            sage: G = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)")
     1309            sage: G.is_even()
     1310            False
     1311        """
     1312        return False
     1313
     1314    def to_even_subgroup(self,relabel=True):
     1315        r"""
     1316        Returns the group with `-Id` added in it.
     1317
     1318        EXAMPLES::
     1319
     1320            sage: G = Gamma1(3).as_permutation_group()
     1321            sage: G.to_even_subgroup()
     1322            Arithmetic subgroup with permutations of right cosets
     1323             S2=(1,3)(2,4)
     1324             S3=(1,2,3)
     1325             L=(2,3,4)
     1326             R=(1,4,2)
     1327        """
     1328        N = self.index()
     1329
     1330        # build equivalence classes in e
     1331        s2 = self._S2
     1332        e = set([])
     1333        for i in xrange(N):
     1334            j = s2[s2[i]]
     1335            if i < j:
     1336                e.add((i,j))
     1337
     1338        # build index for equivalence classes
     1339        e2i = [None]*N  # eq. class to index
     1340        for i,(j0,j1) in enumerate(e):
     1341            e2i[j0] = i
     1342            e2i[j1] = i
     1343   
     1344        # build the quotient permutations
     1345        ss2 = [None]*(N/2)
     1346        ss3 = [None]*(N/2)
     1347        ll = [None]*(N/2)
     1348        rr = [None]*(N/2)
     1349
     1350        s3 = self._S3
     1351        l = self._L
     1352        r = self._R
     1353        for (j0,j1) in e:
     1354            ss2[e2i[j0]] = e2i[s2[j0]]
     1355            ss3[e2i[j0]] = e2i[s3[j0]]
     1356            ll[e2i[j0]] = e2i[l[j0]]
     1357            rr[e2i[j0]] = e2i[r[j0]]
     1358
     1359        G = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)
     1360        if relabel:
     1361            G.relabel()
     1362        return G
     1363
     1364    def nu2(self):
     1365        r"""
     1366        Return the number of elliptic points of order 2.
     1367
     1368        EXAMPLES::
     1369
     1370            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
     1371            sage: G.nu2()
     1372            0
     1373
     1374            sage: G = Gamma1(2).as_permutation_group()
     1375            sage: G.nu2()
     1376            1
     1377        """
     1378        return sum(1 for c in self.S2().cycle_tuples() if len(c) == 2)
     1379
     1380    def nu3(self):
     1381        r"""
     1382        Return the number of elliptic points of order 3.
     1383
     1384        EXAMPLES::
     1385
     1386            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
     1387            sage: G.nu3()
     1388            2
     1389
     1390            sage: G = Gamma1(3).as_permutation_group()
     1391            sage: G.nu3()
     1392            1
     1393        """
     1394        return sum(1 for c in self.S3().cycle_tuples() if len(c) == 2)
     1395
     1396    def nirregcusps(self):
     1397        r"""
     1398        Return the number of irregular cusps.
     1399       
     1400        The cusps are associated to cycles of the permutations `L` or `R`.
     1401        The irregular cusps are the one which are stabilised by `-Id`.
     1402
     1403        EXAMPLES::
     1404               
     1405            sage: S2 = "(1,3,2,4)(5,7,6,8)(9,11,10,12)"
     1406            sage: S3 = "(1,3,5,2,4,6)(7,9,11,8,10,12)"
     1407            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     1408            sage: G.nirregcusps()
     1409            3
     1410        """
     1411        inv = self.S2()**2
     1412        n = 0
     1413        for c in self.L().cycle_tuples(singletons=True):
     1414            if inv(c[0]) in c:
     1415                n += 1
     1416        return n
     1417
     1418    def nregcusps(self):
     1419        r"""
     1420        Return the number of regular cusps of the group.
     1421
     1422        The cusps are associated to cycles of `L` or `R`. The irregular cusps
     1423        correspond to the ones which are not stabilised by `-Id`.
     1424
     1425        EXAMPLES::
     1426
     1427            sage: G = Gamma1(3).as_permutation_group()
     1428            sage: G.nregcusps()
     1429            2
     1430        """
     1431        inv = self.S2()**2
     1432        n = 0
     1433        for c in self.L().cycle_tuples(singletons=True):
     1434            if inv(c[0]) not in c:
     1435                n += 1
     1436        return n//2
     1437
     1438    def cusp_widths(self,exp=False):
     1439        r"""
     1440        Return the list of cusp widths.
     1441
     1442        INPUT:
     1443
     1444        ``exp`` - boolean (default: False) - if True, return a dictionnary with
     1445        keys the possible widths and with values the number of cusp with that
     1446        width.
     1447
     1448        EXAMPLES::
     1449
     1450            sage: G = Gamma1(5).as_permutation_group()
     1451            sage: G.cusp_widths()
     1452            [1, 1, 5, 5]
     1453            sage: G.cusp_widths(exp=True)
     1454            {1: 2, 5: 2}
     1455        """
     1456        inv = self.S2()**2
     1457        L = self.L()
     1458        cusps = set(c[0] for c in L.cycle_tuples(singletons=True))
     1459        if exp:
     1460            widths = {}
     1461        else:
     1462            widths = []
     1463
     1464        while cusps:
     1465            c0 = cusps.pop()
     1466            c = L.orbit(c0)
     1467            if inv(c0) not in c:
     1468                c1 = min(L.orbit(inv(c0)))
     1469                cusps.remove(c1)
     1470                if exp:
     1471                    if not len(c) in widths:
     1472                        widths[len(c)] = 0
     1473                    widths[len(c)] += 1
     1474                else:
     1475                    widths.append(len(c))
     1476            else:
     1477                if exp:
     1478                    if not len(c)/2 in widths:
     1479                        widths[len(c)/2] = 0
     1480                    widths[len(c)/2] += 1
     1481                else:
     1482                    widths.append(len(c)/2)
     1483
     1484        if exp:
     1485            return widths
     1486        return sorted(widths)
     1487
     1488    def ncusps(self):
     1489        r"""
     1490        Returns the number of cusps.
     1491
     1492        EXAMPLES::
     1493
     1494            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2,3,4)",S3="(1,3)(2,4)")
     1495            sage: G.ncusps()
     1496            1
     1497
     1498            sage: G = Gamma1(3).as_permutation_group()
     1499            sage: G.ncusps()
     1500            2
     1501        """
     1502        inv = self.S2()**2
     1503        n = 0
     1504        m = 0
     1505        for c in self.L().cycle_tuples(singletons=True):
     1506            if inv(c[0]) in c:
     1507                n += 1
     1508            else:
     1509                m += 1
     1510        return n + m//2
     1511
     1512    def is_congruence(self):
     1513        r"""
     1514        Test whether self is a congruence group.
     1515
     1516        An odd group is *congruence* if, when we add to it the element `-Id` it
     1517        contains a congruence subgroup `\Gamma(n)` for a certain `n`.
     1518
     1519        EXAMPLES::
     1520
     1521            sage: S2 = '(1,6,4,3)(2,7,5,10)(8,9,11,12)'
     1522            sage: S3 = '(1,2,3,4,5,6)(7,8,9,10,11,12)'
     1523            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3); G
     1524            Arithmetic subgroup with permutations of right cosets
     1525             S2=(1,6,4,3)(2,7,5,10)(8,9,11,12)
     1526             S3=(1,2,3,4,5,6)(7,8,9,10,11,12)
     1527             L=(2,3,10,8)(5,6,7,11)(9,12)
     1528             R=(1,7,9,2)(4,10,12,5)(8,11)
     1529            sage: G.is_congruence()
     1530            True
     1531        """
     1532        return self.to_even_subgroup().is_congruence()
     1533
     1534class EvenArithmeticSubgroup_Permutation(ArithmeticSubgroup_Permutation_class):
    381535    r"""
    391536    An arithmetic subgroup `\Gamma` defined by two permutations, giving the
    40     action of the parabolic generators
     1537    action of four standard generators
    411538   
    42     .. math::
    43        
    44         L = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad R = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}
     1539    .. MATH::
     1540
     1541        s_2 = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix},\quad
     1542        s_3 = \begin{pmatrix} 0 & 1 \\ -1 & 1 \end{pmatrix},\quad
     1543        l = \begin{pmatrix} 1 & 1 \\ 0 & 1\end{pmatrix},\quad
     1544        r = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}.
    451545
    461546    by right multiplication on the coset representatives `\Gamma \backslash {\rm SL}_2(\ZZ)`. 
    471547   
    481548
    491549    EXAMPLES:
    501550
    51     We construct a noncongruence subgroup of index 7 (the smallest possible)::
     1551    Construct a noncongruence subgroup of index 7 (the smallest possible)::
    521552
    53         sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(5,6)]); a3 = SymmetricGroup(7)([(1,3,5),(2,6,7)])
    54         sage: G = ArithmeticSubgroup_Permutation(a2*a3, ~a2 * ~a3); G
    55         Arithmetic subgroup corresponding to permutations L=(1,6)(2,3,4,5,7), R=(1,7,6,3,4)(2,5)
     1553        sage: a2 = SymmetricGroup(7)([(1,2),(3,4),(6,7)]); a3 = SymmetricGroup(7)([(1,2,3),(4,5,6)])
     1554        sage: G = ArithmeticSubgroup_Permutation(S2=a2, S3=a3); G
     1555        Arithmetic subgroup with permutations of right cosets
     1556        S2=(1,2)(3,4)(6,7)
     1557        S3=(1,2,3)(4,5,6)
     1558        L=(1,4,7,6,5,3)
     1559        R=(2,4,5,7,6,3)
    561560        sage: G.index()
    571561        7
    581562        sage: G.dimension_cusp_forms(4)
     
    601564        sage: G.is_congruence()
    611565        False
    621566
    63     We convert some standard congruence subgroups into permutation form::
     1567    Convert some standard congruence subgroups into permutation form::
    641568
    65         sage: Gamma0(12).as_permutation_group()
    66         Arithmetic subgroup corresponding to permutations L=(2,3,4,5,6,7,8,9,10,11,12,13)(14,15,16)(17,19,20,18)(21,23,22), R=(1,3,14,17,21,7,24,9,23,20,16,13)(4,18,12)(5,22,11,15)(6,10,19)
     1569        sage: G = Gamma0(8).as_permutation_group()
     1570        sage: G.index()
     1571        12
     1572        sage: G.is_congruence()
     1573        True
     1574
     1575        sage: G = Gamma0(12).as_permutation_group()
     1576        sage: print G
     1577        Arithmetic subgroup of index 24
     1578        sage: G.is_congruence()
     1579        True
    671580
    681581    The following is the unique index 2 even subgroup of `{\rm SL}_2(\ZZ)`::
    691582
    701583        sage: w = SymmetricGroup(2)([2,1])
    71         sage: G = ArithmeticSubgroup_Permutation(w, w)
     1584        sage: G = ArithmeticSubgroup_Permutation(L=w, R=w)
    721585        sage: G.dimension_cusp_forms(6)
    731586        1
    741587        sage: G.genus()
    751588        0
     1589    """
     1590    def __init__(self, S2, S3, L, R, canonical_labels=False):
     1591        r"""
     1592        TESTS::
    761593
    77     We test unpickling::
    78 
    79         sage: G == loads(dumps(G))
    80         True
    81         sage: G is loads(dumps(G))
    82         False
    83     """
    84 
    85     def __init__(self, L, R):
    86         r"""
    87         Create an arithmetic subgroup given two permutations.
    88        
    89             sage: w = SymmetricGroup(2)([2,1])
    90             sage: ArithmeticSubgroup_Permutation(w, w) # indirect doctest
    91             Arithmetic subgroup corresponding to permutations L=(1,2), R=(1,2)
     1594            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
     1595            sage: G == loads(dumps(G))
     1596            True
     1597            sage: G is loads(dumps(G))
     1598            False
    921599        """
    93 
    94         from sage.groups.perm_gps.all import PermutationGroup
    95         self.L = L
    96         self.R = R
    97         A1=(L*R**(-1)*L)**2
    98         A2=(R**(-1)*L)**3
    99         if A1.order() == 1 and A2.order() == 1:
    100             # This forces G to be even! -- check this.
    101             self._permgp = PermutationGroup([L, R])
    102             if not self._permgp.is_transitive():
    103                 raise ValueError, "Permutations are not transitive"
    104         else:
    105             raise ValueError, "Permutations do not satisfy defining relation"
     1600        self._S2 = S2
     1601        self._S3 = S3
     1602        self._L = L
     1603        self._R = R
     1604        if canonical_labels:
     1605            self._canonical_label_group = self
    1061606
    1071607    def __reduce__(self):
    1081608        r"""
    109         Return the data used to construct self. Used in pickling.
     1609        Data for pickling.
    1101610
    111         EXAMPLE::
     1611        TESTS::
     1612           
     1613            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)")
     1614            sage: G == loads(dumps(G)) #indirect doctest
     1615            True
     1616            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,4)",relabel=True)
     1617            sage: GG = loads(dumps(G))
     1618            sage: G == GG #indirect doctest
     1619            True
     1620            sage: GG.relabel(inplace=False) is GG
     1621            True
     1622        """
     1623        if hasattr(self, '_canonical_label_group'):
     1624            canonical_labels = (self is self._canonical_label_group)
     1625        else:
     1626            canonical_labels = False
     1627        return (EvenArithmeticSubgroup_Permutation,
     1628                (self._S2, self._S3, self._L, self._R, canonical_labels))
    1121629
    113             sage: w = SymmetricGroup(2)([2,1])
    114             sage: ArithmeticSubgroup_Permutation(w, w).__reduce__()
    115             (<class 'sage.modular.arithgroup.arithgroup_perm.ArithmeticSubgroup_Permutation'>, ((1,2), (1,2)))
     1630    def is_odd(self):
     1631        r"""
     1632        Returns True if this subgroup does not contain the matrix `-Id`.
     1633
     1634        EXAMPLES::
     1635
     1636            sage: G = Gamma(2).as_permutation_group()
     1637            sage: G.is_odd()
     1638            False
    1161639        """
    117         return (ArithmeticSubgroup_Permutation, (self.L, self.R))
     1640        return False
    1181641
    119     def perm_group(self):
     1642    def is_even(self):
    1201643        r"""
    121         Return the underlying permutation group.
     1644        Returns True if this subgroup contains the matrix `-Id`.
    1221645
    123         EXAMPLE::
    124            
    125             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    126             sage: ap.HsuExample10().perm_group()
    127             Permutation Group with generators [(1,4)(2,5,9,10,8)(3,7,6), (1,7,9,10,6)(2,3)(4,5,8)]
     1646        EXAMPLES::
     1647       
     1648            sage: G = Gamma(2).as_permutation_group()
     1649            sage: G.is_even()
     1650            True
    1281651        """
    129         return self._permgp
     1652        return True
    1301653
    131     def index(self):
     1654    def nu2(self):
    1321655        r"""
    133         Return the index of self in the full modular group.
     1656        Returns the number of orbits of elliptic points of order 2 for this
     1657        arithmetic subgroup.
    1341658
    135         EXAMPLE::
    136            
    137             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    138             sage: ap.HsuExample18().index()
    139             18
     1659        EXAMPLES::
     1660
     1661            sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
     1662            sage: G.nu2()
     1663            2
    1401664        """
    141         return self._permgp.degree()
     1665        return sum(1 for i in xrange(self.index()) if self._S2[i] == i)
    1421666
    143     def _repr_(self):
     1667    def nu3(self):
    1441668        r"""
    145         String representation of self.
     1669        Returns the number of orbits of elliptic points of order 3 for this
     1670        arithmetic subgroup.
    1461671
    147         EXAMPLE::
     1672        EXAMPLES::
     1673            sage: G = ArithmeticSubgroup_Permutation(S2="(1,4)(2)(3)",S3="(1,2,3)(4)")
     1674            sage: G.nu3()
     1675            1
     1676        """
     1677        return sum(1 for i in xrange(self.index()) if self._S3[i] == i)
    1481678
    149             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    150             sage: ap.HsuExample18()._repr_()
    151             'Arithmetic subgroup corresponding to permutations L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18), R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)'
     1679    def ncusps(self):
     1680        r"""
     1681        Return the number of cusps of this arithmetic subgroup.
     1682       
     1683        EXAMPLES::
     1684
     1685            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)",S3="(1,2,3)(4,5,6)")
     1686            sage: G.ncusps()
     1687            3
    1521688        """
    153         return "Arithmetic subgroup corresponding to permutations L=%s, R=%s" % (self.L, self.R)
     1689        return len(self.L().cycle_tuples(True))
    1541690
    155     def permutation_action(self, x):
     1691    def _spanning_tree_kulkarni(self, root=0, on_right=True):
    1561692        r"""
    157         Given an element x of `{\rm SL}_2(\ZZ)`, compute the
    158         permutation of the cosets of self given by right multiplication by x.
     1693        Returns a spanning tree for the coset graph of the group for the
     1694        generators `S2` and `S3`.
     1695
     1696        Warning: the output is randomized in order to be able to obtain any
     1697        spanning tree of the coset graph. The algorithm mainly follows
     1698        Kulkarni's paper.
     1699   
     1700        INPUT:
     1701
     1702        - ``on_right`` - boolean (default: True) - if False, return spanning
     1703          tree for the left cosets.
     1704
     1705        OUTPUT:
     1706
     1707        - ``tree`` - a spanning tree of the graph associated to the action of
     1708          ``L`` and ``S2`` on the cosets
     1709         
     1710        - ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)`` - representatives of the
     1711          cosets with respect to the spanning tree
     1712         
     1713        - ``word_reps`` - list of lists with ``s2`` and ``s3`` - word
     1714          representatives of the cosets with respect to the spanning tree.
    1591715       
    160         EXAMPLE::
     1716        - ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in
     1717          the graph which are not in the spanning tree.
    1611718
    162             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    163             sage: ap.HsuExample10().permutation_action(SL2Z([32, -21, -67, 44]))
    164             (1,10,5,6,3,8,9,2,7,4)
     1719        EXAMPLES::
     1720
     1721            sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
     1722            sage: tree,reps,wreps,gens = G._spanning_tree_kulkarni()
     1723            sage: tree
     1724            Digraph on 4 vertices
     1725            sage: for m in reps: print m,"\n****"
     1726            [1 0]
     1727            [0 1]
     1728            ****
     1729            [ 0  1]
     1730            [-1  1]
     1731            ****
     1732            [-1  1]
     1733            [-1  0]
     1734            ****
     1735            [1 1]
     1736            [0 1]
     1737            ****
     1738            sage: for w in wreps: print ','.join(w)
     1739            <BLANKLINE>
     1740            s3
     1741            s3,s3
     1742            s3,s3,s2
     1743            sage: gens
     1744            [(0, 1, 's2'), (3, 3, 's3')]
    1651745        """
    166         x = SL2Z(x)
    167         w = sl2z_word_problem(x)
    168         p = LREvalPerm(w, self.L, self.R)
    169         return p
     1746        from sage.graphs.digraph import DiGraph
     1747        from sage.misc.prandom import randint
    1701748
    171     def __call__(self, g, check=True):
     1749        N = len(self._S2)
     1750
     1751        if on_right:
     1752            s2 = self._S2
     1753            s3 = self._S3
     1754
     1755        else:
     1756            s2 = [None] * N
     1757            s3 = [None] * N
     1758            for i in xrange(N):
     1759                s2[self._S2[i]] = i
     1760                s3[self._S3[i]] = i
     1761
     1762        # the tree and the lift
     1763        tree = DiGraph(multiedges=False,loops=False)
     1764        gens = []
     1765
     1766        reps = [None]*self.index()
     1767        word_reps = [None]*self.index()
     1768        reps[root] = SL2Z(1)
     1769        word_reps[root] = []
     1770
     1771        x0 = root
     1772        tree.add_vertex(x0)
     1773        l = [x0]
     1774
     1775        while True:
     1776            # complete the current 3-loop in the tree
     1777            if s3[x0] != x0: # loop of length 3
     1778                x1 = s3[x0]
     1779                x2 = s3[x1]
     1780                tree.add_edge(x0,x1,'s3')
     1781                tree.add_edge(x1,x2,'s3')
     1782                if on_right:
     1783                    reps[x1] = reps[x0] * S3m
     1784                    reps[x2] = reps[x1] * S3m
     1785                    word_reps[x1] = word_reps[x0] + ['s3']
     1786                    word_reps[x2] = word_reps[x1] + ['s3']
     1787                else:
     1788                    reps[x1] = S3m * reps[x0]
     1789                    reps[x2] = S3m * reps[x1]
     1790                    word_reps[x1] = ['s3'] + word_reps[x0]
     1791                    word_reps[x2] = ['s3'] + word_reps[x1]
     1792                l.append(x1)
     1793                l.append(x2)
     1794            else: # elliptic generator
     1795                gens.append((x0,x0,'s3'))
     1796
     1797            # now perform links with s while we find another guy
     1798            while l:
     1799                x1 = l.pop(randint(0,len(l)-1))
     1800                x0 = s2[x1]
     1801
     1802                if x1 != x0: # loop of length 2
     1803                    if x0 in tree:
     1804                        gens.append((x1,x0,'s2'))
     1805                        del l[l.index(x0)] # x0 must be in l
     1806                    else:
     1807                        tree.add_edge(x1,x0,'s2')
     1808                        if on_right:
     1809                            reps[x0] = reps[x1] * S2m
     1810                            word_reps[x0] = word_reps[x1] + ['s2']
     1811                        else:
     1812                            reps[x0] = S2m * reps[x1]
     1813                            word_reps[x0] = ['s2'] + word_reps[x1]
     1814                        break
     1815                else: # elliptic generator
     1816                    gens.append((x1,x1,'s2'))
     1817                       
     1818            else:
     1819                break
     1820           
     1821        return tree,reps,word_reps,gens
     1822
     1823    def _spanning_tree_verrill(self, root=0, on_right=True):
    1721824        r"""
    173         Create an element of this group from g. If check=True (the default),
    174         perform the (possibly rather computationally-intensive) check to make
    175         sure g is in this group.
     1825        Returns a spanning tree with generators `S2` and `L`.
    1761826
    177         EXAMPLE::
    178            
    179             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    180             sage: ap.HsuExample10()(SL2Z([1,1,0,1]))
    181             Traceback (most recent call last):
    182             ...
    183             TypeError: Not in group
    184             sage: ap.HsuExample10()(SL2Z([1,1,0,1]), check=False)
     1827        The algorithm follows the one of Helena Verrill.
     1828
     1829        OUTPUT:
     1830       
     1831        - ``tree`` - a spanning tree of the graph associated to the action of
     1832          ``L`` and ``S2`` on the cosets
     1833         
     1834        - ``reps`` - list of matrices in `{\rm SL}_2(\ZZ)` - representatives of the
     1835          cosets with respect to the spanning tree
     1836         
     1837        - ``word_reps`` - list of string with ``s`` and ``l`` - word
     1838          representatives of the cosets with respect to the spanning tree.
     1839       
     1840        - ``gens`` - list of 3-tuples ``(in,out,label)`` - the list of edges in
     1841          the graph which are not in the spanning tree.
     1842
     1843        EXAMPLES::
     1844
     1845            sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
     1846            sage: tree,reps,wreps,gens=G._spanning_tree_verrill()
     1847            sage: tree
     1848            Digraph on 4 vertices
     1849            sage: for m in reps: print m, "\n****"
     1850            [1 0]
     1851            [0 1]
     1852            ****
     1853            [ 0 -1]
     1854            [ 1  0]
     1855            ****
     1856            [1 2]
     1857            [0 1]
     1858            ****
    1851859            [1 1]
    1861860            [0 1]
     1861            ****
     1862            sage: wreps
     1863            ['', 's', 'll', 'l']
     1864            sage: gens
     1865            [(2, 0, 'l'), (1, 1, 'l'), (2, 3, 's')]
     1866
     1867        TODO:
     1868
     1869        Take care of the shape of the spanning tree as in Helena Verrill's program.
    1871870        """
    188         g = SL2Z(g)
    189         if (not check) or (self.permutation_action(g)(1) == 1):
    190             g._parent = self
    191             return g
     1871        from sage.misc.prandom import randint
     1872        from copy import copy
     1873
     1874        if on_right:
     1875            s = self._S2
     1876            l = self._L
    1921877        else:
    193             raise TypeError, "Not in group"
     1878            s = [None]*self.index()
     1879            l = [None]*self.index()
     1880            for i in xrange(self.index()):
     1881                s[self._S2[i]] = i
     1882                l[self._L[i]] = i
    1941883
    195     def __cmp__(self, other):
     1884        from sage.graphs.digraph import DiGraph
     1885        tree = DiGraph(multiedges=False,loops=False)
     1886        gens = []
     1887
     1888        reps = [None]*self.index()
     1889        word_reps = [None]*self.index()
     1890        reps[root] = SL2Z(1)
     1891        word_reps[root] = ''
     1892
     1893        x0 = root
     1894        tree.add_vertex(x0)
     1895        waiting = [x0]
     1896
     1897        while True:
     1898            # complete the current l-loop in the tree from x0
     1899            x = x0
     1900            xx = l[x]
     1901            while xx != x0:
     1902                tree.add_edge(x,xx,'l')
     1903                if on_right:
     1904                    reps[xx] = reps[x] * Lm
     1905                    word_reps[xx] = word_reps[x] + 'l'
     1906                else:
     1907                    reps[xx] = Lm * reps[x]
     1908                    word_reps[xx] = 'l' + word_reps[x]
     1909                waiting.append(xx)
     1910                x = xx
     1911                xx = l[x]
     1912
     1913            gens.append((x,x0,'l'))
     1914
     1915            # now perform links with s while we find another guy which will
     1916            # become the new x0
     1917            while waiting:
     1918                x0 = None
     1919                while waiting and x0 is None:
     1920                    x1 = waiting.pop(randint(0,len(waiting)-1))
     1921                    x0 = s[x1]
     1922
     1923                if x0 is not None:
     1924                    if x1 != x0: # loop of length 2
     1925                        if x0 in tree:
     1926                            gens.append((x1,x0,'s'))
     1927                            if x0 in waiting:
     1928                                del waiting[waiting.index(x0)] # x0 must be in l
     1929                        else:
     1930                            tree.add_edge(x1,x0,'s')
     1931                            if on_right:
     1932                                reps[x0] = reps[x1] * S2m
     1933                                word_reps[x0] = word_reps[x1] + 's'
     1934                            else:
     1935                                reps[x0] = S2m * reps[x1]
     1936                                word_reps[x0] = 's' + word_reps[x1]
     1937                            break
     1938                    else: # elliptic generator
     1939                        gens.append((x1,x1,'s'))
     1940                       
     1941            else:
     1942                break
     1943           
     1944        return tree,reps,word_reps,gens
     1945
     1946    def todd_coxeter_s2_s3(self):
    1961947        r"""
    197         Compare self to other.
     1948        Returns a 4-tuple ``(coset_reps, gens, s2, s3)`` where ``coset_reps``
     1949        are coset representatives of the subgroup, ``gens`` is a list of
     1950        generators, ``s2`` and ``s3`` are the action of the matrices `S2` and
     1951        `S3` on the list of cosets.
     1952
     1953        The so called *Todd-Coxeter algorithm* is a general method for coset
     1954        enumeration for a subgroup of a group given by generators and relations.
    1981955
    1991956        EXAMPLES::
    200            
    201             sage: G = ArithmeticSubgroup_Permutation(SymmetricGroup(2)([2,1]), SymmetricGroup(2)([2,1]))
    202             sage: cmp(G, 1) in [1,-1]
     1957
     1958            sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
     1959            sage: G.genus()
     1960            0
     1961            sage: reps,gens,s2,s3=G.todd_coxeter_s2_s3()
     1962            sage: g1,g2 = gens
     1963            sage: g1 in G and g2 in G
    2031964            True
    204             sage: cmp(G, Gamma0(8)) in [-1,1]
    205             True
    206             sage: cmp(G, G)
    207             0
     1965            sage: g1
     1966            [-1  0]
     1967            [ 1 -1]
     1968            sage: g2
     1969            [-1  3]
     1970            [-1  2]
     1971            sage: S2 = SL2Z([0,-1,1,0])
     1972            sage: S3 = SL2Z([0,1,-1,1])
     1973            sage: reps[0] == SL2Z([1,0,0,1])
     1974            True
     1975            sage: all(reps[i]*S2*~reps[s2[i]] in G for i in xrange(4))
     1976            True
     1977            sage: all(reps[i]*S3*~reps[s3[i]] in G for i in xrange(4))
     1978            True
    2081979        """
    209         if not isinstance(other, ArithmeticSubgroup_Permutation):
    210             return cmp(type(self), type(other))
     1980        tree,reps,wreps,edges = self._spanning_tree_kulkarni()
     1981
     1982        gens = []
     1983        for e in edges:
     1984            if e[2] == 's2':
     1985                gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
     1986            elif e[2] == 's3':
     1987                gens.append(self(reps[e[0]] * S3m * ~reps[e[1]]))
     1988            else:
     1989                raise ValueError, "this should not happen"
     1990
     1991        return reps, gens, self._S2[:], self._S3[:]
     1992
     1993    def todd_coxeter_l_s2(self):
     1994        r"""
     1995        Returns a 4-tuple ``(coset_reps, gens, l, s2)`` where ``coset_reps`` is
     1996        a list of coset representatives of the subgroup, ``gens`` a list of
     1997        generators, ``l`` and ``s2`` are list that corresponds to the action of
     1998        the matrix `S2` and `L` on the cosets.
     1999
     2000        EXAMPLES::
     2001
     2002            sage: G = ArithmeticSubgroup_Permutation(S2='(1,2)(3,4)',S3='(1,2,3)')
     2003            sage: reps,gens,l,s=G.todd_coxeter_l_s2()
     2004            sage: reps
     2005            [[1 0]
     2006            [0 1], [ 0 -1]
     2007            [ 1  0], [1 2]
     2008            [0 1], [1 1]
     2009            [0 1]]
     2010            sage: gens
     2011            [[1 3]
     2012            [0 1], [ 1 0]
     2013            [-1  1], [ 2 -3]
     2014            [ 1 -1]]
     2015            sage: l
     2016            [3, 1, 0, 2]
     2017            sage: s
     2018            [1, 0, 3, 2]
     2019            sage: S2 = SL2Z([0,-1,1,0])
     2020            sage: L = SL2Z([1,1,0,1])
     2021            sage: reps[0] == SL2Z([1,0,0,1])
     2022            True
     2023            sage: all(reps[i]*S2*~reps[s[i]] in G for i in xrange(4))
     2024            True
     2025            sage: all(reps[i]*L*~reps[l[i]] in G for i in xrange(4))
     2026            True
     2027        """
     2028        tree,reps,wreps,edges = self._spanning_tree_verrill()
     2029
     2030        gens = []
     2031        for e in edges:
     2032            if e[2] == 'l':
     2033                gens.append(self(reps[e[0]] * Lm * ~reps[e[1]]))
     2034            elif e[2] == 's':
     2035                gens.append(self(reps[e[0]] * S2m * ~reps[e[1]]))
     2036            else:
     2037                raise ValueError, "this should not happen"
     2038
     2039        return reps, gens, self._L[:], self._S2[:]
     2040
     2041    todd_coxeter = todd_coxeter_l_s2
     2042
     2043    def coset_reps(self):
     2044        r"""
     2045        Return coset representatives.
     2046
     2047        EXAMPLES::
     2048
     2049            sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)")
     2050            sage: c = G.coset_reps()
     2051            sage: len(c)
     2052            4
     2053            sage: [g in G for g in c]
     2054            [True, False, False, False]
     2055        """
     2056        return self.todd_coxeter()[0]
     2057
     2058    def cusp_widths(self,exp=False):
     2059        r"""
     2060        Return the list of cusp widths of the group.
     2061
     2062        EXAMPLES::
     2063
     2064            sage: G = Gamma(2).as_permutation_group()
     2065            sage: G.cusp_widths()
     2066            [2, 2, 2]
     2067            sage: G.cusp_widths(exp=True)
     2068            {2: 3}
     2069
     2070            sage: S2 = "(1,2)(3,4)(5,6)"
     2071            sage: S3 = "(1,2,3)(4,5,6)"
     2072            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     2073            sage: G.cusp_widths()
     2074            [1, 1, 4]
     2075            sage: G.cusp_widths(exp=True)
     2076            {1: 2, 4: 1}
     2077
     2078            sage: S2 = "(1,2)(3,4)(5,6)"
     2079            sage: S3 = "(1,3,5)(2,4,6)"
     2080            sage: G = ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     2081            sage: G.cusp_widths()
     2082            [6]
     2083            sage: G.cusp_widths(exp=True)
     2084            {6: 1}
     2085        """
     2086        seen = [True]*self.index()
     2087
     2088        if exp:
     2089            widths = {}
    2112090        else:
    212             return cmp( (self.L, self.R), (other.L, other.R) )
     2091            widths = []
     2092        for i in xrange(self.index()):
     2093            if seen[i]:
     2094                seen[i] = False
     2095                j = self._L[i]
     2096                n = 1
     2097                while j != i:
     2098                    seen[j] = False
     2099                    n += 1
     2100                    j = self._L[j]
     2101                if exp:
     2102                    if n not in widths:
     2103                        widths[n] = 0
     2104                    widths[n] += 1
     2105                else:
     2106                    widths.append(n)
    2132107
     2108        if exp:
     2109            return widths
     2110        return sorted(widths)
     2111 
    2142112    def is_congruence(self):
    2152113        r"""
    216         Return True if this is a congruence subgroup. Uses Hsu's algorithm, as
    217         implemented by Chris Kurth in KFarey.
     2114        Return True if this is a congruence subgroup.
     2115       
     2116        ALGORITHM:
     2117       
     2118        Uses Hsu's algorithm, as implemented by Chris Kurth in KFarey.
    2182119
    2192120        EXAMPLES:
    2202121
     2122        Test if `{\rm SL}_2(\ZZ)` is congruence::
     2123
     2124            sage: a = ArithmeticSubgroup_Permutation(L='',R='')
     2125            sage: a.index()
     2126            1
     2127            sage: a.is_congruence()
     2128            True
     2129
    2212130        This example is congruence -- it's Gamma0(3) in disguise: ::
    2222131           
    223             sage: import sage.modular.arithgroup.arithgroup_perm as ap
    224             sage: G=ap.ArithmeticSubgroup_Permutation(SymmetricGroup(4)((2,3,4)), SymmetricGroup(4)((1,3,4))); G
    225             Arithmetic subgroup corresponding to permutations L=(2,3,4), R=(1,3,4)
     2132            sage: S2 = SymmetricGroup(4)
     2133            sage: l = S2((2,3,4))
     2134            sage: r = S2((1,3,4))
     2135            sage: G = ArithmeticSubgroup_Permutation(L=l,R=r)
     2136            sage: print G
     2137            Arithmetic subgroup with permutations of right cosets
     2138             S2=(1,2)(3,4)
     2139             S3=(1,4,2)
     2140             L=(2,3,4)
     2141             R=(1,3,4)
    2262142            sage: G.is_congruence()
    2272143            True
    2282144
     
    2322148            sage: ap.HsuExample10().is_congruence()
    2332149            False
    2342150        """
    235         L = self.L
    236         R = self.R
    237         N=L.order()
    238         e=2**(ZZ(N).valuation(2))
    239         m=N/e
    240         if e==1:     #i.e. N is odd
    241             onehalf=int(Integers(N)(2)**(-1))   #i.e. 2^(-1) mod N
    242             check=(R**2*L**(-onehalf))**3
    243             if check.order() == 1:
    244                 return True       #Congruence
    245             else:
    246                 return False      #Noncongruence
    247         elif m==1:     #i.e. N is a power of 2
    248             onefifth=int(Integers(N)(5)**(-1))   #i.e. 2^(-1) mod N
    249             S=L**20*R**onefifth*L**(-4)*R**(-1)
    250             #Congruence if these three permutations are trivial:
    251             Rel1=(L*R**(-1)*L)**(-1) * S * (L*R**(-1)*L) * S
    252             Rel2=S**(-1)*R*S*R**(-25)
    253             Rel3=(S*R**5*L*R**(-1)*L)**3
    254             if Rel1.order()==1 and Rel2.order()==1 and Rel3.order()==1:
    255                 return True       #Congruence
    256             else:
    257                 return False      #Noncongruence
    258         else:         #i.e. e>1, M>1
    259             onehalf=int(Integers(m)(2)**(-1))    #i.e. 2^(-1)  mod m
    260             onefifth=int(Integers(e)(5)**(-1))   #i.e. 2^(-1)  mod e
    261             c=int(Integers(m)(e)**(-1))*e        #i.e. e^(-1)e mod m
    262             d=int(Integers(e)(m)**(-1))*m        #i.e. m^(-1)m mod e
     2151        if self.index() == 1:  # the group is SL2Z (trivial case)
     2152            return True
     2153
     2154        L = self.L()              # action of L
     2155        R = self.R()              # action of R
     2156
     2157        N = L.order() # generalised level of the group
     2158
     2159        # write N as N = em where e = 2^k and m odd
     2160        e = 1
     2161        m = N
     2162        while m%2 == 0:
     2163            m //= 2
     2164            e *= 2
     2165
     2166        if e==1:     # N is odd
     2167            onehalf = int(~Zmod(N)(2))   #i.e. 2^(-1) mod N
     2168            rel = (R*R*L**(-onehalf))**3
     2169            return rel.is_one()
     2170
     2171        elif m==1:     # N is a power of 2
     2172            onefifth=int(~Zmod(N)(5))   #i.e. 5^(-1) mod N
     2173            S=L**20*R**onefifth*L**(-4)*~R
     2174
     2175            # congruence if the three below permutations are trivial
     2176            rel=(~L*R*~L) * S * (L*~R*L) * S
     2177            if not rel.is_one():
     2178                return False
     2179
     2180            rel=~S*R*S*R**(-25)
     2181            if not rel.is_one():
     2182                return False
     2183
     2184            rel=(S*R**5*L*~R*L)**3
     2185            if not rel.is_one():
     2186                return False
     2187
     2188            return True
     2189
     2190        else:         # e>1, m>1
     2191            onehalf=int(~Zmod(m)(2))    #i.e. 2^(-1)  mod m
     2192            onefifth=int(~Zmod(e)(5))   #i.e. 5^(-1)  mod e
     2193            c=int(~Zmod(m)(e))*e        #i.e. e^(-1)e mod m
     2194            d=int(~Zmod(e)(m))*m        #i.e. m^(-1)m mod e
    2632195            a=L**c
    2642196            b=R**c
    2652197            l=L**d
    2662198            r=R**d
    267             s=l**20*r**onefifth*l**(-4)*r**(-1)
     2199            s=l**20*r**onefifth*l**(-4)*~r
    2682200
    269             Rel1=a**(-1)*r**(-1)*a*r
    270             Rel2=(a*b**(-1)*a)**4
    271             Rel3=(a*b**(-1)*a)**2*(b**(-1)*a)**(-3)
    272             Rel4=(a*b**(-1)*a)**2*(b**2*a**(-onehalf))**(-3)
    273             Rel5=(l*r**(-1)*l)**(-1)*s*(l*r**(-1)*l)*s
    274             Rel6=s**(-1)*r*s*r**(-25)
    275             Rel7=(l*r**(-1)*l)**2*(s*r**5*l*r**(-1)*l)**(-3)
    276 
    277             if (Rel1.order()==1
    278                 and Rel2.order()==1
    279                 and Rel3.order()==1
    280                 and Rel4.order()==1
    281                 and Rel5.order()==1
    282                 and Rel6.order()==1
    283                 and Rel7.order()==1):
    284                 return True
    285             else:
     2201            #Congruence if the seven permutations below are trivial:
     2202            rel=~a*~r*a*r
     2203            if not rel.is_one():
    2862204                return False
    2872205
     2206            rel=(a*~b*a)**4
     2207            if not rel.is_one():
     2208                return False
    2882209
    289 def LREvalPerm(w, L, R):
    290     r"""
    291     Given a word w as output by sl2z_word_problem, evaluate the word with the
    292     given permutations for L and R. Because we are dealing with a right rather
    293     than a left action, arguments are evaluated back to front.
     2210            rel=(a*~b*a)**2*(~a*b)**3
     2211            if not rel.is_one():
     2212                return False
    2942213
    295     EXAMPLE::
     2214            rel=(a*~b*a)**2*(b*b*a**(-onehalf))**(-3)
     2215            if not rel.is_one():
     2216                return False
    2962217
    297         sage: import  sage.modular.arithgroup.arithgroup_perm as ap
    298         sage: L = SymmetricGroup(4)('(1,2)(3,4)'); R = SymmetricGroup(4)('(1,2,3,4)')
    299         sage: ap.LREvalPerm([(1,1),(0,1)], L, R) ==  L * R
    300         True
    301     """
    302     from sage.groups.perm_gps.all import PermutationGroup
    303     G=PermutationGroup([L,R])       #Change this?
    304     Id=G.identity()
    305     L=G(L)
    306     R=G(R)
     2218            rel=(~l*r*~l)*s*(l*~r*l)*s
     2219            if not rel.is_one():
     2220                return False
    3072221
    308     checkM=Id
     2222            rel=~s*r*s*r**(-25)
     2223            if not rel.is_one():
     2224                return False
    3092225
    310     for i in range(len(w)):
    311         if w[i][0]==0:
    312             c=int(w[i][1])
    313             checkM=L**c*checkM
    314         elif w[i][0]==1:
    315             c=int(w[i][1])
    316             checkM=R**c*checkM
    317     return checkM
     2226            rel=(l*~r*l)**2*(s*r**5*l*~r*l)**(-3)
     2227            if not rel.is_one():
     2228                return False
    3182229
    319 def sl2z_word_problem(A, max_iterations=20):
    320     r"""
    321     Given an element of SL2Z, express it as a word in the generators L =
    322     [1,1,0,1] and R = [1,0,1,1].
     2230            return True
    3232231
    324     The return format is a list of pairs (a,b), where a = 0 or 1 denoting L or
    325     R respectively, and b is an integer exponent.
    3262232
    327     The parameter iterations (default 20) controls the maximum number of
    328     iterations to allow in the program's main loop; an error is raised if the
    329     algorithm has not terminated after this many iterations.
    330 
    331     EXAMPLE::
    332 
    333         sage: import sage.modular.arithgroup.arithgroup_perm as ap
    334         sage: L = SL2Z([1,1,0,1]); R = SL2Z([1,0,1,1])
    335         sage: ap.sl2z_word_problem(L)
    336         [(0, 1)]
    337         sage: ap.sl2z_word_problem(R**(-1))
    338         [(1, -1)]
    339         sage: ap.sl2z_word_problem(L*R)
    340         [(0, 1), (1, -1), (0, 0), (1, 2)]
    341     """
    342 
    343     A = SL2Z(A)
    344     output=[]
    345 
    346     ## If A00 is zero, add A01 to it (probably a better way to do this)
    347     if A[0,0]==0:
    348         A=A*Rm
    349         output=[(1,-1)]+output
    350 
    351     if A[0,0]<0:   # Make sure A00 is positive
    352         A=SL2Z(-1)*A
    353         output= [(1,-1), (0,1), (1,-1), (0,1), (1,-1), (0,1)] + output
    354 
    355     if A[0,1]<0:   # if A01 is negative make it positive
    356         n=(-A[0,1]/A[0,0]).ceil()  #n s.t. 0 <= A[0,1]+n*A[0,0] < A[0,0]
    357         A=A*Lm**n
    358         output=[(0, -n)]+output
    359    ## At this point A00>0 and A01>=0
    360     timesthrough=0
    361     while not (A[0,0]==0 or A[0,1]==0):
    362         if A[0,0]>A[0,1]:
    363             n=(A[0,0]/A[0,1]).floor()
    364             A=A*Rm**(-n)
    365             output=[(1, n)]+output
    366 
    367         else:      #A[0,0]<A[0,1]
    368             n=(A[0,1]/A[0,0]).floor()
    369             A=A*Lm**(-n)
    370             output=[(0, n)]+output
    371         if timesthrough > max_iterations:
    372             raise ValueError, "Over-recursion"
    373         timesthrough=timesthrough+1
    374 
    375     if A==SL2Z(1):
    376         pass       #done, so don't add R^0
    377     elif A[0,0]==0:
    378         c=A[1,1]
    379         A=A*Lm**(c-1)*Rm*Lm**(-1)
    380         output=[(0,1),(1,-1),(0, 1-c)]+output
    381     else:
    382         c=A[1,0]
    383         A=A*Rm**(-c)
    384         output=[(1,c)]+output
    385 
    386     return output
    387 
    388 def eval_word(B):
    389     r"""
    390     Given a word in the format output by sl2z_word_problem, convert it back
    391     into an element of SL2(Z).
    392 
    393     EXAMPLES::
    394    
    395         sage: import sage.modular.arithgroup.arithgroup_perm as ap
    396         sage: ap.eval_word([(0, 1), (1, -1), (0, 0), (1, 3), (0, 2), (1, 9), (0, -1)])
    397         [ 66 -59]
    398         [ 47 -42]
    399     """
    400     checkM=SL2Z(1)
    401     for i in range(len(B)):
    402         if B[i][0]==0:
    403             c=int(B[i][1])
    404             checkM=checkM*Lm**c
    405         elif B[i][0]==1:
    406             c=int(B[i][1])
    407             checkM=checkM*Rm**c
    408     return checkM
    4092233
    4102234def HsuExample10():
    4112235    r"""
     
    4152239       
    4162240        sage: import sage.modular.arithgroup.arithgroup_perm as ap
    4172241        sage: ap.HsuExample10()
    418         Arithmetic subgroup corresponding to permutations L=(1,4)(2,5,9,10,8)(3,7,6), R=(1,7,9,10,6)(2,3)(4,5,8)
     2242        Arithmetic subgroup with permutations of right cosets
     2243         S2=(1,2)(3,4)(5,6)(7,8)(9,10)
     2244         S3=(1,8,3)(2,4,6)(5,7,10)
     2245         L=(1,4)(2,5,9,10,8)(3,7,6)
     2246         R=(1,7,9,10,6)(2,3)(4,5,8)
    4192247    """
    420     from sage.groups.perm_gps.all import SymmetricGroup
    421     return ArithmeticSubgroup_Permutation(SymmetricGroup(10)("(1,4)(2,5,9,10,8)(3,7,6)"), SymmetricGroup(10)("(1,7,9,10,6)(2,3)(4,5,8)"))
     2248    return ArithmeticSubgroup_Permutation(
     2249            L="(1,4)(2,5,9,10,8)(3,7,6)",
     2250            R="(1,7,9,10,6)(2,3)(4,5,8)",
     2251            relabel=False)
    4222252
    4232253def HsuExample18():
    4242254    r"""
     
    4282258       
    4292259        sage: import sage.modular.arithgroup.arithgroup_perm as ap
    4302260        sage: ap.HsuExample18()
    431         Arithmetic subgroup corresponding to permutations L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18), R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)
     2261        Arithmetic subgroup with permutations of right cosets
     2262         S2=(1,5)(2,11)(3,10)(4,15)(6,18)(7,12)(8,14)(9,16)(13,17)
     2263         S3=(1,7,11)(2,18,5)(3,9,15)(4,14,10)(6,17,12)(8,13,16)
     2264         L=(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)
     2265         R=(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)
    4322266    """
    433     from sage.groups.perm_gps.all import SymmetricGroup
    434     return ArithmeticSubgroup_Permutation(SymmetricGroup(18)("(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)"), SymmetricGroup(18)("(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)"))
    435 
    436 def convert_to_permgroup(G):
    437     r"""
    438     Given an arbitrary arithmetic subgroup, convert it to permutation form.
    439 
    440     Note that the permutation representation is not always unique, so if G is
    441     already of permutation type, then the return value won't necessarily be
    442     identical to G, but it will represent the same subgroup.
    443 
    444     EXAMPLES::
    445 
    446         sage: import sage.modular.arithgroup.arithgroup_perm as ap
    447         sage: ap.convert_to_permgroup(Gamma0(5))
    448         Arithmetic subgroup corresponding to permutations L=(2,3,4,5,6), R=(1,3,5,4,6)
    449         sage: ap.convert_to_permgroup(ap.HsuExample10())
    450         Arithmetic subgroup corresponding to permutations L=(1,2)(3,5,6,7,8)(4,9,10), R=(1,9,6,7,10)(2,5,8)(3,4)
    451     """
    452 
    453     from all import is_ArithmeticSubgroup
    454     from sage.groups.perm_gps.all import SymmetricGroup
    455     if not is_ArithmeticSubgroup(G):
    456         raise TypeError, "%s is not an arithmetic subgroup of SL2(Z)" % G
    457 
    458     if not G.is_even():
    459         raise NotImplementedError, "Only subgroups containing -1 are currently implemented"
    460 
    461     X = list(G.coset_reps())
    462 
    463     if not (X[0] in G):
    464         raise Exception, "Coset reps wrongly ordered!"
    465 
    466     # find permutation action of L
    467     perm = []
    468     for i in xrange(len(X)):
    469         for j in xrange(len(X)):
    470             if (X[i] * Lm) * X[j]**(-1) in G:
    471                 perm.append(j)
    472                 break
    473         if len(perm) != i+1:
    474             raise ArithmeticError
    475     Lperm = SymmetricGroup(len(X))([i+1 for i in perm])
    476 
    477     # same for R
    478     perm = []
    479     for i in xrange(len(X)):
    480         for j in xrange(len(X)):
    481             if (X[i] * Rm) * X[j]**(-1) in G:
    482                 perm.append(j)
    483                 break
    484         if len(perm) != i+1:
    485             raise ArithmeticError
    486     Rperm = SymmetricGroup(len(X))([i+1 for i in perm])
    487     return ArithmeticSubgroup_Permutation(Lperm, Rperm)
     2267    return ArithmeticSubgroup_Permutation(
     2268            L="(1,2)(3,4)(5,6,7)(8,9,10)(11,12,13,14,15,16,17,18)",
     2269            R="(1,12,18)(2,6,13,9,4,8,17,7)(3,16,14)(5,11)(10,15)",
     2270            relabel=False)
  • new file sage/modular/arithgroup/tests.py

    diff -r ebb9a83e8edb -r 35716f045476 sage/modular/arithgroup/tests.py
    - +  
     1r"""
     2Testing Arithmetic subgroup
     3"""
     4################################################################################
     5#
     6#       Copyright (C) 2009, The Sage Group -- http://www.sagemath.org/
     7#
     8#  Distributed under the terms of the GNU General Public License (GPL)
     9#
     10#  The full text of the GPL is available at:
     11#
     12#                  http://www.gnu.org/licenses/
     13#
     14################################################################################
     15
     16from arithgroup_perm import ArithmeticSubgroup_Permutation, EvenArithmeticSubgroup_Permutation, OddArithmeticSubgroup_Permutation
     17from sage.modular.arithgroup.all import Gamma, Gamma0, Gamma1, GammaH
     18from sage.rings.finite_rings.integer_mod_ring import Zmod
     19
     20import sage.misc.prandom as prandom
     21from sage.misc.misc import cputime
     22
     23def random_even_arithgroup(index,nu2_max=None,nu3_max=None):
     24    r"""
     25    Return a random even arithmetic subgroup
     26
     27    EXAMPLES::
     28
     29        sage: import sage.modular.arithgroup.tests as tests
     30        sage: tests.random_even_arithgroup(30) #random
     31        Arithmetic subgroup of index 30
     32    """
     33    from sage.groups.perm_gps.permgroup import PermutationGroup
     34
     35    test = False
     36
     37    if nu2_max is None:
     38        nu2_max = index//5
     39    if nu3_max is None:
     40        nu3_max = index//7
     41
     42    while not test:
     43        nu2 = prandom.randint(0,nu2_max)
     44        nu2 = index%2 + nu2*2
     45        nu3 = prandom.randint(0,nu3_max)
     46        nu3 = index%3 + nu3*3
     47
     48        l = range(1,index+1)
     49        prandom.shuffle(l)
     50        S2 = []
     51        for i in xrange(nu2):
     52            S2.append((l[i],))
     53        for i in xrange(nu2,index,2):
     54            S2.append((l[i],l[i+1]))
     55        prandom.shuffle(l)
     56        S3 = []
     57        for i in xrange(nu3):
     58            S3.append((l[i],))
     59        for i in xrange(nu3,index,3):
     60            S3.append((l[i],l[i+1],l[i+2]))
     61        G = PermutationGroup([S2,S3])
     62        test = G.is_transitive()
     63
     64    return ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
     65
     66class Test:
     67    r"""
     68    Testing class for arithmetic subgroup implemented via permutations.
     69    """
     70    def __init__(self, index=20, index_max=50):
     71        r"""
     72        Create an arithmetic subgroup testing object.
     73
     74        INPUT:
     75
     76        - ``index`` - the index of random subgroup to test
     77
     78        - ``index_max`` - the maximum index for congruence subgroup to test
     79
     80        EXAMPLES::
     81
     82            sage: from sage.modular.arithgroup.tests import Test
     83            sage: Test()
     84            Arithmetic subgroup testing class
     85        """
     86        self.congroups = []
     87        i = 1
     88        while Gamma(i).index() < index_max:
     89            self.congroups.append(Gamma(i))
     90            i += 1
     91        i = 1
     92        while Gamma0(i).index() < index_max:
     93            self.congroups.append(Gamma0(i))
     94            i += 1
     95        i = 2
     96        while Gamma1(i).index() < index_max:
     97            self.congroups.append(Gamma1(i))
     98            M = Zmod(i)
     99            U = filter(lambda x: x.is_unit(), M)
     100            for j in xrange(1,len(U)-1):
     101                self.congroups.append(GammaH(i,prandom.sample(U,j)))
     102            i += 1
     103
     104        self.index = index
     105
     106    def __repr__(self):
     107        r"""
     108        Return the string representation of self
     109
     110        EXAMPLES::
     111
     112            sage: from sage.modular.arithgroup.tests import Test
     113            sage: Test().__repr__()
     114            'Arithmetic subgroup testing class'
     115        """
     116        return "Arithmetic subgroup testing class"
     117
     118    def _do(self, name):
     119        """
     120        Perform the test 'test_name', where name is specified as an
     121        argument. This function exists to avoid a call to eval.
     122
     123        EXAMPLES::
     124
     125            sage: from sage.modular.arithgroup.tests import Test
     126            sage: Test()._do("random")
     127            test_random
     128            ...
     129        """
     130
     131        print "test_%s"%name
     132        Test.__dict__["test_%s"%name](self)
     133
     134    def random(self, seconds=0):
     135        """
     136        Perform random tests for a given number of seconds, or
     137        indefinitely if seconds is not specified.
     138
     139        EXAMPLES::
     140
     141            sage: from sage.modular.arithgroup.tests import Test
     142            sage: Test().random(1)
     143            test_random
     144            ...
     145        """
     146        self.test("random", seconds)
     147       
     148    def test(self, name, seconds=0):
     149        """
     150        Repeatedly run 'test_name', where name is passed as an
     151        argument. If seconds is nonzero, run for that many seconds. If
     152        seconds is 0, run indefinitely.
     153
     154        EXAMPLES::
     155
     156            sage: from sage.modular.arithgroup.tests import Test
     157            sage: T = Test()
     158            sage: T.test('relabel',seconds=1)
     159            test_relabel
     160            ...
     161            sage: T.test('congruence_groups',seconds=1)
     162            test_congruence_groups
     163            ...
     164            sage: T.test('contains',seconds=1)
     165            test_contains
     166            ...
     167            sage: T.test('todd_coxeter',seconds=1)
     168            test_todd_coxeter
     169            ...
     170        """
     171        seconds = float(seconds)
     172        total = cputime()
     173        n = 1
     174        while seconds == 0 or cputime(total) < seconds:
     175            s = "** test_dimension: number %s"%n
     176            if seconds > 0:
     177                s += " (will stop after about %s seconds)"%seconds
     178            t = cputime()
     179            self._do(name)
     180            print "\ttime=%s\telapsed=%s"%(cputime(t),cputime(total))
     181            n += 1
     182
     183    def test_random(self):
     184        """
     185        Do a random test from all the possible tests.
     186
     187        EXAMPLES::
     188
     189            sage: from sage.modular.arithgroup.tests import Test
     190            sage: Test().test_random() #random
     191            Doing random test
     192        """
     193        tests = [a for a in Test.__dict__.keys() if a[:5] == "test_" and a != "test_random"]
     194        name = prandom.choice(tests)
     195        print "Doing random test %s"%name
     196        Test.__dict__[name](self)
     197
     198    def test_relabel(self):
     199        r"""
     200        Try the function canonic labels for a random even modular subgroup.
     201
     202        EXAMPLES::
     203
     204            sage: from sage.modular.arithgroup.tests import Test
     205            sage: Test().test_relabel() # random
     206        """
     207        G = random_even_arithgroup(self.index)
     208        G.relabel()
     209        s2 = G._S2
     210        s3 = G._S3
     211        l = G._L
     212        r = G._R
     213
     214        p = range(1,self.index)
     215
     216        for _ in xrange(10):
     217            prandom.shuffle(p)
     218            pp = [0] + p
     219            ss2 = [None]*self.index
     220            ss3 = [None]*self.index
     221            ll = [None]*self.index
     222            rr = [None]*self.index
     223            for i in xrange(self.index):
     224                ss2[pp[i]] = pp[s2[i]]
     225                ss3[pp[i]] = pp[s3[i]]
     226                ll[pp[i]] = pp[l[i]]
     227                rr[pp[i]] = pp[r[i]]
     228            if G.is_even():
     229                GG = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)
     230            else:
     231                GG = OddArithmeticSubgroup_Permutation(ss2,ss3,ll,rr)
     232            GG.relabel()
     233
     234            for elt in ['_S2','_S3','_L','_R']:
     235                if getattr(G,elt) != getattr(GG,elt):
     236                    print "s2 = %s" %str(s2)
     237                    print "s3 = %s" %str(s3)
     238                    print "ss2 = %s" %str(ss2)
     239                    print "ss3 = %s" %str(ss3)
     240                    print "pp = %s" %str(pp)
     241                    raise AssertionError, "%s does not coincide" %elt
     242
     243    def test_congruence_groups(self):
     244        r"""
     245        Check whether the different implementations of methods for congruence
     246        groups and generic arithmetic group by permutations return the same
     247        results.
     248
     249        EXAMPLES::
     250
     251            sage: from sage.modular.arithgroup.tests import Test
     252            sage: Test().test_congruence_groups() #random
     253        """
     254        G = prandom.choice(self.congroups)
     255        GG = G.as_permutation_group()
     256
     257        if not GG.is_congruence():
     258            raise AssertionError, "Hsu congruence test failed"
     259
     260        methods = [
     261            'index',
     262            'is_odd',
     263            'is_even',
     264            'is_normal',
     265            'ncusps',
     266            'nregcusps',
     267            'nirregcusps',
     268            'nu2',
     269            'nu3',
     270            'generalised_level']
     271
     272        for f in methods:
     273            if getattr(G,f)() != getattr(GG,f)():
     274                raise AssertionError, "results of %s does not coincide for %s" %(f,G)
     275
     276        if sorted((G.cusp_width(c) for c in G.cusps())) != GG.cusp_widths():
     277            raise AssertionError, "Cusps widths are different for %s" %G
     278       
     279        for _ in xrange(20):
     280            m = GG.random_element()
     281            if m not in G:
     282                raise AssertionError, "random element generated by perm. group not in %s" %(str(m),str(G))
     283
     284    def test_contains(self):
     285        r"""
     286        Test whether the random generator for arithgroup perms gives matrices in
     287        the group.
     288
     289        EXAMPLES::
     290
     291            sage: from sage.modular.arithgroup.tests import Test
     292            sage: Test().test_contains() #random
     293        """
     294        G = random_even_arithgroup(self.index)
     295
     296        for _ in xrange(20):
     297            g = G.random_element()
     298            if G.random_element() not in G:
     299                raise AssertionError, "%s not in %s" %(g,G)
     300
     301    def test_spanning_trees(self):
     302        r"""
     303        Test coset representatives obtained from spanning trees for even
     304        subgroup (Kulkarni's method with generators ``S2``, ``S3`` and Verrill's
     305        method with generators ``L``, ``S2``).
     306
     307        EXAMPLES::
     308
     309            sage: from sage.modular.arithgroup.tests import Test
     310            sage: Test().test_spanning_trees() #random
     311        """
     312        from sage.all import prod
     313        from all import SL2Z
     314        from arithgroup_perm import S2m,S3m,Lm
     315
     316        G = random_even_arithgroup(self.index)
     317
     318        m = {'l':Lm, 's':S2m}
     319        tree,reps,wreps,gens = G._spanning_tree_verrill()
     320        assert reps[0] == SL2Z([1,0,0,1])
     321        assert wreps[0] == ''
     322        for i in xrange(1,self.index):
     323            assert prod(m[letter] for letter in wreps[i]) == reps[i]
     324        tree,reps,wreps,gens = G._spanning_tree_verrill(on_right=False)
     325        assert reps[0] == SL2Z([1,0,0,1])
     326        assert wreps[0] == ''
     327        for i in xrange(1,self.index):
     328            assert prod(m[letter] for letter in wreps[i]) == reps[i]
     329
     330        m = {'s2':S2m, 's3':S3m}
     331        tree,reps,wreps,gens = G._spanning_tree_kulkarni()
     332        assert reps[0] == SL2Z([1,0,0,1])
     333        assert wreps[0] == []
     334        for i in xrange(1,self.index):
     335            assert prod(m[letter] for letter in wreps[i]) == reps[i]
     336        tree,reps,wreps,gens = G._spanning_tree_kulkarni(on_right=False)
     337        assert reps[0] == SL2Z([1,0,0,1])
     338        assert wreps[0] == []
     339        for i in xrange(1,self.index):
     340            assert prod(m[letter] for letter in wreps[i]) == reps[i]
     341
     342    def test_todd_coxeter(self):
     343        r"""
     344        Test representatives of todd coxeter algorithm
     345
     346        EXAMPLES::
     347
     348            sage: from sage.modular.arithgroup.tests import Test
     349            sage: Test().test_todd_coxeter() #random
     350        """
     351        from all import SL2Z
     352        from arithgroup_perm import S2m,S3m,Lm,Rm
     353
     354        G = random_even_arithgroup(self.index)
     355
     356        reps,gens,l,s2 = G.todd_coxeter_l_s2()
     357        assert reps[0] == SL2Z([1,0,0,1])
     358        assert len(reps) == G.index()
     359        for i in xrange(1,len(reps)):
     360            assert reps[i] not in G
     361            assert reps[i]*S2m*~reps[s2[i]] in G
     362            assert reps[i]*Lm*~reps[l[i]] in G
     363            for j in xrange(i+1,len(reps)):
     364                assert reps[i] * ~reps[j] not in G
     365                assert reps[j] * ~reps[i] not in G
     366
     367        reps,gens,s2,s3 = G.todd_coxeter_s2_s3()
     368        assert reps[0] == SL2Z([1,0,0,1])
     369        assert len(reps) == G.index()
     370        for i in xrange(1,len(reps)):
     371            assert reps[i] not in G
     372            assert reps[i]*S2m*~reps[s2[i]] in G
     373            assert reps[i]*S3m*~reps[s3[i]] in G
     374            for j in xrange(i+1,len(reps)):
     375                assert reps[i] * ~reps[j] not in G
     376                assert reps[j] * ~reps[i] not in G
     377