r"""
"Named" Permutation grops (such as the symmetric group, S_n)
           
You can construct the following permutation groups:

-- SymmetricGroup, $S_n$ of order $n!$ (n can also be a list $X$ of distinct 
                   positive integers, in which case it returns $S_X$)
      
-- AlternatingGroup, $A_n$ or order $n!/2$ (n can also be a list $X$ 
                   of distinct positive integers, in which case it returns 
                   $A_X$)
       
-- DihedralGroup, $D_n$ of order $2n$
       
-- CyclicPermutationGroup, $C_n$ of order $n$
       
-- TransitiveGroup, $i^{th}$ transitive group of degree $n$ 
                      from the GAP tables of transitive groups (requires
                      the "optional" package database_gap)

-- MathieuGroup(degree), Mathieu group of degree 9, 10, 11, 12, 21, 22, 23, or 24.

-- KleinFourGroup, subgroup of $S_4$ of order $4$ which is not $C_2 \times C_2$

-- PGL(n,q), projective general linear group of $n\times n$ matrices over 
             the finite field GF(q)

-- PSL(n,q), projective special linear group of $n\times n$ matrices over 
             the finite field GF(q)

-- PSp(2n,q), projective symplectic linear group of $2n\times 2n$ matrices 
              over the finite field GF(q)

-- PSU(n,q), projective special unitary group of $n\times n$ matrices having
             coefficients in the finite field $GF(q^2)$ that respect a 
             fixed nondegenerate sesquilinear form, of determinant 1.

-- PGU(n,q), projective general unitary group of $n\times n$ matrices having
             coefficients in the finite field $GF(q^2)$ that respect a 
             fixed nondegenerate sesquilinear form, modulo the centre.

-- SuzukiGroup(q), Suzuki group over GF(q), $^2 B_2(2^{2k+1}) = Sz(2^{2k+1})$.


AUTHOR:
    - David Joyner (2007-06): split from permgp.py (suggested by Nick Alexander)
    - Matthew Fix  (2008-06): Added visualization features for the subgroups of SO(R, 3)
    
REFERENCES:
    Cameron, P., Permutation Groups. New York: Cambridge University Press, 1999.
    Wielandt, H., Finite Permutation Groups. New York: Academic Press, 1964.
    Dixon, J. and Mortimer, B., Permutation Groups, Springer-Verlag, Berlin/New York, 1996. 

"""

#*****************************************************************************
#       Copyright (C) 2006 William Stein <wstein@gmail.com>
#                          David Joyner <wdjoyner@gmail.com>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#                  http://www.gnu.org/licenses/
#*****************************************************************************


import random
from types import ListType

import sage.structure.element as element
import sage.groups.group as group

from sage.rings.all      import RationalField, Integer
from sage.interfaces.all import gap, is_GapElement, is_ExpectElement
import sage.structure.coerce as coerce
from sage.rings.finite_field import FiniteField as GF
from sage.rings.arith import factor
from sage.groups.abelian_gps.abelian_group import AbelianGroup
from sage.misc.functional import is_even, log
from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic

def permutation_list_to_degree(v):
    try:
        v = [Integer(z) for z in v]
    except TypeError:
        raise ValueError, "each entry of list must be an integer"
    if min(v) < 1:
        raise ValueError, "each element of list must be positive"
    return max(v), v



     
class VisualizeGeo():
    """
    Class inherited by the subgroups of SO(R, 3).  Performs common functions in their visualization.
    
    """

    def setRotate(self, r):
        """
        Sets the rotate list to be the given list or tuple r.
        Elements of r should be element of the group in cycle or
        symbolic form.    
        Deletes all previous rotations.  
        
        Examples:
            sage:  R = CyclicPermutationGroup(8)
            sage:  R.setRotate([(2 0 1 90 10), (1, 0, 1, 180, 20)]
        """
        self.rotate  = []
        for i in r:
            self.addRotate(i)
    def addRotations(self, elements):
        """
        Add a list or tuple of rotations to the current set of rotations.  Rotations will be shown
        in order that they are input.  elements may be either a power of x or a 
        group element in cycle notation. 
        Examples:
            sage: R = CyclicPermutationGroup(8)
            sage: R.addRotate([x, x^2])
        """
        for i in elements:
            self.addRotate(i)

    
    def show(self, **kwds):
        """
        Shows the figure and executes the rotation list
        Accepts all the usual keyword arguments that the show() command does
        for Graphics3d objects
        default aspect_ratio = [1, 1, 1]
        default frame = False        

        Examples:
            sage:  R = OctahedralGroup(5)
            sage:  R.addRotate(R[2])
            sage:  R.show(
          
        """
        if self.P is None:
            self.geo()
        if kwds.has_key('frame'): 
            frame = kwds['frame']
            del kwds['frame']	
        else:
            frame = False
        if kwds.has_key('aspect_ratio'):
            aspect_ratio = kwds['aspect_ratio']
            del kwds['aspect_ratio']
        else:
            aspect_ratio = [1, 1, 1]
        self.P.show(rotate = self.rotate, blink = (), frame = frame, aspect_ratio = aspect_ratio, **kwds)
        


class SymmetricGroup(PermutationGroup_generic):
    """
    The full symmetric group of order $n!$, as a permutation group.
    (If n is a list of positive integers then it returns the
    symmetric group of the associated set.) 

    INPUT:
       n -- a positive integer 

    EXAMPLE:
        sage: G = SymmetricGroup(8)
        sage: G.order()
        40320
        sage: G
        Symmetric group of order 8! as a permutation group
        sage: G.degree()
        8
        sage: S8 = SymmetricGroup(8)
        sage: loads(dumps(S8)) == S8
        True
        sage: G = SymmetricGroup([1,2,4,5])
        sage: G
        Symmetric group of order 4! as a permutation group
        sage: G.set()
        [1, 2, 4, 5]
        sage: G = SymmetricGroup(4)
        sage: G
        Symmetric group of order 4! as a permutation group
        sage: G.set()
        [1, 2, 3, 4]
    """
    def __init__(self, n):
        if isinstance(n, (list, tuple)):
            self._deg, n = permutation_list_to_degree(n)
            PermutationGroup_generic.__init__(self, 'SymmetricGroup(%s)'%n, from_group = True)
            self._set = n
        else:
            try:
                self._deg = n = Integer(n)
                self._set = n
                if n < 1:
                    raise ValueError, "n (=%s) must be >= 1"%n
                PermutationGroup_generic.__init__(self, 'SymmetricGroup(%s)'%n, from_group = True)
            except TypeError, msg:
                raise ValueError, "%s\nn (=%s) must be an integer >= 1 or a list (but n has type %s)"%(msg, n,type(n))

    def _num_symbols(self):
        try:
            return self.__num_symbols
        except AttributeError:
            self.__num_symbols = len(self._set) if isinstance(self._set,ListType) else self.degree()
        return self.__num_symbols    

    def _repr_(self):
        return "Symmetric group of order %s! as a permutation group"%self._num_symbols()

    def __str__(self):
        """
        EXAMPLES:
            sage: S = SymmetricGroup([2,3,7]); S
            Symmetric group of order 3! as a permutation group
            sage: str(S)
            'SymmetricGroup([2, 3, 7])'
            sage: S = SymmetricGroup(5); S
            Symmetric group of order 5! as a permutation group
            sage: str(S)
            'SymmetricGroup(5)'
        """
        if isinstance(self._set, ListType):
            x = self._set
        else:
            x = self.degree()
        return "SymmetricGroup(%s)"%x


    def set(self):
        if isinstance(self._set, list):
            return self._set
        else:
            return range(1, self._deg + 1)	

class OctahedralGroup(VisualizeGeo, SymmetricGroup):
    """
    Builds the subgroup of SO(R, 3) of order 24.  Is provably isomorphic 
    to the SymmetricGroup on 4 elements and is here realized the symmetries of either
    a cube or an octahedron.  
    

    Example:
        sage: O = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
    """    


    def __init__(self):
        self.cycle_to_tuple = {}  
        self.cycle_to_action_oct = {}
        self.cycle_to_action_cube = {}
        self.cube = None
        self.oct = None
        self.rotate = []
        self.showCube = True  #Else show Octahedron
        
        SymmetricGroup.__init__(self, 4)
        

        self.vertices_cube = [(.7, -.7, -.7), (.7, .7, -.7), (-.7, .7, -.7), (-.7, -.7, -.7), (.7, -.7, .7), (.7, .7, .7), (-.7, .7, .7), (-.7, -.7, .7)]
        self.vertices_oct = [(0, 0, -1), (0, 0, 1), (1, 0, 0), (0, 1, 0), (-1, 0, 0), (0, -1, 0)]
        
    def set_geo(fig):
        """
        Input either oct, octahedron, or cube to set that 
        figure to be the current figure to show.  Note that even
        if one is set current, the other still is updated with added rotate 
        commands, since these are shared.  Also, after geo_cube() is called, this
        is set to show the cube, and similarly for geo_oct()
         
        Examples:
            sage:  T = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
            sage:  T.set_geo('oct')
        """

        if fig == 'oct' or fig == 'octahedron':
            self.showCube = False
        elif fig == 'cube':
            self.showCube = True
        else:
            raise ValueError


    def build(self):
        """
        Initialize the cycle_to_action and cycle_to_tuple dictionaries

        Example:
            sage: T = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
            sage: T.build()
        """

        x = self.gens()[0]
        y = self.gens()[1]
        
        o = self.vertices_oct
        c = self.vertices_cube

        # Identity
        self.cycle_to_action_oct[y**2] = 'Rotation by 0.0 degrees about the axis defined by the points (%s, %s, %s) and (%s, %s, %s)' % (0, 0, 1, 0, 0, -1)
        self.cycle_to_action_cube[y**2] = 'Rotation by 0.0 degrees about the midpoint of the plane defined by (%s, %s, %s) and (%s, %s, %s) and its opposite plane' % (.7, -.7, -.7, .7, .7, -.7) 
        self.cycle_to_tuple[y**2] = (0, 0, 1, 0, 20)

        # Build 3 cyclic subgroups of order 4
        for i in range(1, 4):
            degrees = 90*i

            self.cycle_to_action_oct[x**i] = 'Rotation by %s degrees about the axis defined by the points (%s, %s, %s) and (%s, %s, %s)' %(degrees, o[0][0], o[0][1], o[0][2], o[1][0],o[1][1], o[1][2])
            self.cycle_to_action_cube[x**i] = 'Rotation by %s degrees about the midpoint of the plane defined by (%s, %s, %s) and (%s, %s, %s) and its opposite plane' %(degrees, c[4][0], c[4][1], c[4][2], c[6][0], c[6][1], c[6][2])
            self.cycle_to_tuple[x**i] = (o[2][0], o[2][1], o[2][2], degrees, 20)


            self.cycle_to_action_oct[(x*x*y)**i] = 'Rotation by %s degrees about the axis defined by the points (%s, %s, %s) and (%s, %s, %s)' %(degrees, o[2][0], o[2][1], o[2][2], o[4][0],o[4][1], o[4][2]) 
            self.cycle_to_action_cube[(x*x*y)**i] = 'Rotation by %s degrees about the midpoint of the plane defined by (%s, %s, %s) and (%s, %s, %s) and its opposite plane' %(degrees, c[0][0], c[0][1], c[0][2], c[5][0], c[5][1], c[5][2])
            self.cycle_to_tuple[(x*x*y)**i] = (o[2][0], o[2][1], o[2][2], degrees, 20)

             
            self.cycle_to_action_oct[(x*y*x)**i] = 'Rotation by %s degrees about the axis defined by the points (%s, %s, %s) and (%s, %s, %s)' %(degrees, o[3][0], o[3][1], o[3][2], o[5][0],o[5][1], o[5][2]) 
            self.cycle_to_action_cube[(x*y*x)**i] = 'Rotation by %s degrees about the midpoint of the plane defined by (%s, %s, %s) and (%s, %s, %s) and its opposite plane' %(degrees, c[1][0], c[1][1], c[1][2], c[6][0], c[6][1], c[6][2])
            self.cycle_to_tuple[(x*y*x)**i] = (o[3][0], o[3][1], o[3][2], degrees, 20)


        # Build 4 cyclic subgroups of order 3
        for i in range(1, 3):
            degrees = 120*i
            
            self.cycle_to_action_oct[(x*y)**i] = 'Rotation by %s degrees about an axis defined by two opposite planes, one with defining points (%s, %s, %s), (%s, %s, %s), (%s, %s, %s)' % (degrees, o[2][0], o[2][1], o[2][2], o[5][0], o[5][1], o[5][2], o[1][0], o[1][1], o[1][2])
            self.cycle_to_action_cube[(x*y)**i] = 'Rotation by %s degrees about an axis defined by (%s, %s, %s) and its diagonally oppostie point' %(degrees, c[4][0], c[4][1], c[4][2])
            self.cycle_to_tuple[(x*y)**i] = (c[4][0], c[4][1], c[4][2], degrees, 20)

             
            self.cycle_to_action_oct[(y*x)**i] = 'Rotation by %s degrees about an axis defined by two opposite planes, one with defining points (%s, %s, %s), (%s, %s, %s), (%s, %s, %s)' % (degrees, o[2][0], o[2][1], o[2][2], o[3][0], o[3][1], o[3][2], o[1][0], o[1][1], o[1][2])
            self.cycle_to_action_cube[(y*x)**i] = 'Rotation by %s degrees about an axis defined by (%s, %s, %s) and its diagonally oppostie point' %(degrees, c[5][0], c[5][1], c[5][2])
            self.cycle_to_tuple[(y*x)**i] = (c[5][0], c[5][1], c[5][2], degrees, 20)

             
            self.cycle_to_action_oct[(x*y*x*x)**i] = 'Rotation by %s degrees about an axis defined by two opposite planes, one with defining points (%s, %s, %s), (%s, %s, %s), (%s, %s, %s)' % (degrees, o[3][0], o[3][1], o[3][2], o[4][0], o[4][1], o[4][2], o[1][0], o[1][1], o[1][2])
            self.cycle_to_action_cube[(x*y*x*x)**i] = 'Rotation by %s degrees about an axis defined by (%s, %s, %s) and its diagonally oppostie point' %(degrees, c[6][0], c[6][1], c[6][2])
            self.cycle_to_tuple[(x*y*x*x)**i] = (c[6][0], c[6][1], c[6][2], degrees, 20)

             
            self.cycle_to_action_oct[(x*x*y*x)**i] = 'Rotation by %s degrees about an axis defined by two opposite planes, one with defining points (%s, %s, %s), (%s, %s, %s), (%s, %s, %s)' % (degrees, o[5][0], o[5][1], o[5][2], o[4][0], o[4][1], o[4][2], o[1][0], o[1][1], o[1][2])
            self.cycle_to_action_cube[(x*x*y*x)**i] = 'Rotation by %s degrees about an axis defined by (%s, %s, %s) and its diagonally oppostie point' %(degrees, c[7][0], c[7][1], c[7][2])
            self.cycle_to_tuple[(x*x*y*x)**i] = (c[7][0], c[7][1], c[7][2], degrees, 20)

        
        # Build 6 Subgroups of order 2

        self.cycle_to_action_oct[y] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[5][0], o[5][1], o[5][2], o[2][0], o[2][1], o[2][2])
        self.cycle_to_action_cube[y] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[0][0], c[0][1], c[0][2], c[4][0], c[4][1], c[4][2])   
        midx = (c[0][0] + c[4][0])/float(2)
        midy = (c[0][1] + c[4][1])/float(2)
        midz = (c[0][2] + c[4][2])/float(2)
        self.cycle_to_tuple[y] = (midx, midy, midz, 180, 20)    
         
        self.cycle_to_action_oct[x*y*x**3] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[3][0], o[3][1], o[3][2], o[2][0], o[2][1], o[2][2])
        self.cycle_to_action_cube[x*y*x**3] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[1][0], c[1][1], c[1][2], c[5][0], c[5][1], c[5][2])   
        midx = (c[1][0] + c[5][0])/float(2)
        midy = (c[1][1] + c[5][1])/float(2)
        midz = (c[1][2] + c[5][2])/float(2)
        self.cycle_to_tuple[x*y*x**3] = (midx, midy, midz, 180, 20)

        self.cycle_to_action_oct[(x**3)*y*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[1][0], o[1][1], o[1][2], o[2][0], o[2][1], o[2][2])
        self.cycle_to_action_cube[(x**3)*y*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[5][0], c[5][1], c[5][2], c[4][0], c[4][1], c[4][2])   
        midx = (c[4][0] + c[5][0])/float(2)
        midy = (c[4][1] + c[5][1])/float(2)
        midz = (c[4][2] + c[5][2])/float(2)
        self.cycle_to_tuple[(x**3)*y*x] = (midx, midy, midz, 180, 20)

        self.cycle_to_action_oct[x*x*y*x*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[3][0], o[3][1], o[3][2], o[1][0], o[1][1], o[1][2])
        self.cycle_to_action_cube[x*x*y*x*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[5][0], c[5][1], c[5][2], c[6][0], c[6][1], c[6][2])   
        midx = (c[5][0] + c[6][0])/float(2)
        midy = (c[5][1] + c[6][1])/float(2)
        midz = (c[5][2] + c[6][2])/float(2)
        self.cycle_to_tuple[x*x*y*x*x] = (midx, midy, midz, 180, 20)

        self.cycle_to_action_oct[y*x*x*y*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[4][0], o[4][1], o[4][2], o[1][0], o[1][1], o[1][2])
        self.cycle_to_action_cube[y*x*x*y*x] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[7][0], c[7][1], c[7][2], c[6][0], c[6][1], c[6][2])   
        midx = (c[7][0] + c[6][0])/float(2)
        midy = (c[7][1] + c[6][1])/float(2)
        midz = (c[7][2] + c[6][2])/float(2)
        self.cycle_to_tuple[y*x*x*y*x] = (midx, midy, midz, 180, 20)

        self.cycle_to_action_oct[x*y*x*x*y] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(o[5][0], o[5][1], o[5][2], o[1][0], o[1][1], o[1][2])
        self.cycle_to_action_cube[x*y*x*x*y] = 'Rotation by 180 degrees about an axis defined by an edge pair one with points (%s, %s, %s) and (%s, %s, %s)' %(c[7][0], c[7][1], c[7][2], c[4][0], c[4][1], c[4][2])   
        midx = (c[4][0] + c[7][0])/float(2)
        midy = (c[4][1] + c[7][1])/float(2)
        midz = (c[4][2] + c[7][2])/float(2)
        self.cycle_to_tuple[x*y*x*x*y] = (midx, midy, midz, 180, 20)   


    def addRotate(self, element):
        """
        Add a rotate to the rotate set, given an element of the group.

        Example:
           sage: T = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
           sage: T.addRotate(T[2])
        """

        if len(self.cycle_to_action_cube) ==0:
            self.build()
        self.rotate.append(self.cycle_to_tuple[element])
    
    def show(self, **kwds):
        """
        Shows the figure and executes the rotation list
        Accepts all the usual keyword arguments that the show() command does
        for Graphics3d objects
        if self.showCube == true, shows the cube, ortherwise the octahedron.
        This value may be set with the set_geo() method
        default aspect_ratio = [1, 1, 1]
        default frame = False        

        Examples:
            sage:  R = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
            sage:  R.addRotate(R[2])
            sage:  R.show()
                   # Shows the octahedron, since this is the last figure to be built
          
        """
        if self.cube is None:
            self.geo_cube()
        if self.oct == None:
            self.geo_oct()
        if kwds.has_key('frame'): 
            frame = kwds['frame']
            del kwds['frame']	
        else:
            frame = False
        if kwds.has_key('aspect_ratio'):
            aspect_ratio = kwds['aspect_ratio']
            del kwds['aspect_ratio']
        else:
            aspect_ratio = [1, 1, 1]
        if self.showCube:
            self.cube.show(rotate = self.rotate, frame = frame, aspect_ratio = aspect_ratio, **kwds)
        else:
            self.oct.show(rotate = self.rotate, frame = frame, aspect_ratio = aspect_ratio, **kwds)

    def geo_cube(self):
        """
        Create and return the cube.  Sets the showCube flag to True.

        Example:
           sage: R = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
           sage: g = T.geo_cube()
           sage: show(g)
        """

        from sage.misc import prandom
        from sage.plot.plot3d import shapes2
        
        for i in range(0, 8):
            self.cube += shapes2.point3d(self.vertices_cube[i], size = 15, color = 255*(prandom.random(), prandom.random(), prandom.random()))
        for j in range(0, 3):
            self.cube += shapes2.line3d([self.vertices_cube[j], self.vertices_cube[1+j]], thickness = 6)
            self.cube += shapes2.line3d([self.vertices_cube[j], self.vertices_cube[4+j]], thickness = 6)
            self.cube += shapes2.line3d([self.vertices_cube[4+j], self.vertices_cube[5+j]], thickness = 6)
        self.cube += shapes2.line3d([self.vertices_cube[4], self.vertices_cube[7]], thickness = 6)
        self.cube += shapes2.line3d([self.vertices_cube[0], self.vertices_cube[3]], thickness = 6)
        self.cube += shapes2.line3d([self.vertices_cube[3], self.vertices_cube[7]], thickness = 6)
        self.showCube = True
        return self.cube
    
    def geo_oct(self):
        """
        Create and return the octahedron.  Sets the showCube flag to False.

        Example:
           sage: R = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
           sage: g = T.geo_oct()
           sage: show(g)
        """

        from sage.misc import prandom
        from sage.plot.plot3d import shapes2

        for i in range(0, 6):
            self.oct += shapes2.point3d(self.vertices_oct[i], size = 15, color = 255*(prandom.random(), prandom.random(), prandom.random()))
        for i in range(0, 4):
            self.oct += shapes2.line3d([self.vertices_oct[0], self.vertices_oct[i+2]], thickness = 6)
            self.oct += shapes2.line3d([self.vertices_oct[1], self.vertices_oct[i+2]], thickness = 6)
        self.oct += shapes2.line3d([self.vertices_oct[2], self.vertices_oct[3]], thickness = 6)
        self.oct += shapes2.line3d([self.vertices_oct[3], self.vertices_oct[4]], thickness = 6)
        self.oct += shapes2.line3d([self.vertices_oct[4], self.vertices_oct[5]], thickness = 6)
        self.oct += shapes2.line3d([self.vertices_oct[5], self.vertices_oct[2]], thickness = 6)
        self.showCube = False
        return self.oct




    
    def getActionCube(self, element):
        """
        Return the action of a given element on the cube.

        Example:
           sage: R = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
           sage: R.getActionCube(R[2])
             'Rotation by 180 degrees about an axis defined by an edge pair one with points (0.7, 0.7, 0.7) and (0.7, -0.7, 0.7)'
        """ 
        if len(self.cycle_to_action_cube) == 0:
            self.build()
        return self.cycle_to_action_cube[element]
   
    def getActionOct(self, element):
        """
        Return the action of a given element on the octahedron.

        Example:
           sage: R = sage.groups.perm_gps.permgroup_named.OctahedralGroup()
           sage: R.getActionOct(R[2])
             'Rotation by 180 degrees about an axis defined by an edge pair one with points (0, 0, 1) and (1, 0, 0)'
        """

        if len(self.cycle_to_action_oct) == 0:
            self.build()
        return self.cycle_to_action_oct[element]







class AlternatingGroup(PermutationGroup_generic):
    """
    The alternating group of order $n!/2$, as a permutation group.

    INPUT:
        n -- integer $n \geq 1$

    EXAMPLE:
        sage: G = AlternatingGroup(8)
        sage: G.order()
        20160
        sage: G
        Alternating group of order 8!/2 as a permutation group
        sage: loads(G.dumps()) == G
        True
        sage: G = AlternatingGroup([1,2,4,5])
        sage: G
        Alternating group of order 4!/2 as a permutation group
        sage: G.set()
        [1, 2, 4, 5]
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n):
        if isinstance(n, (list, tuple)):
            deg, n = permutation_list_to_degree(n)
            PermutationGroup_generic.__init__(self, 'AlternatingGroup(%s)'%n, from_group = True)
            self._set = n
        else:
            try:
                self._deg = n = Integer(n)
                self._set = n
                if n < 1:
                    raise ValueError, "n (=%s) must be >= 1"%n
                PermutationGroup_generic.__init__(self, 'AlternatingGroup(%s)'%n, from_group = True)
            except TypeError, msg:
                raise ValueError, "n (=%s) must be an integer >= 1 or a list"%n

    def _num_symbols(self):
        try:
            return self.__num_symbols
        except AttributeError:
            self.__num_symbols = len(self._set) if isinstance(self._set,ListType) else self.degree()
        return self.__num_symbols    

    def _repr_(self):
        """
        EXAMPLES:
            sage: A = AlternatingGroup([2,3,7]); A
            Alternating group of order 3!/2 as a permutation group
        """
        z = self._num_symbols()
        return "Alternating group of order %s!/2 as a permutation group" % z

    def __str__(self):
        """
        EXAMPLES:
            sage: A = AlternatingGroup([2,3,7]); A
            Alternating group of order 3!/2 as a permutation group
            sage: str(A)
            'AlternatingGroup([2, 3, 7])'
        """
        if isinstance(self._set, ListType):
            x = self._set
        else:
            x = self.degree()
        return "AlternatingGroup(%s)" % x

    def set(self):
        if isinstance(self._set, list):
            return self._set
        else:
            return range(1, self._deg + 1)

class IcosahedralGroup(VisualizeGeo, AlternatingGroup):
    """
    TODO.  Is the last subgroup of SO(R, 3) to be implemented with visualizations.  
    This is next for me (Matthew Fix) to do.
    """
    def __init__(self):
        raise NotImplementedError


class TetrahedralGroup(VisualizeGeo, AlternatingGroup):
    """
    Symmetry group of a regular tetrahedron.  Provably isomorphic to the Alternating Group on 4 elements.
    

    Examples:
       sage: T = sage.groups.perm_gps.permgroup_named.TetrahedralGroup()

    """

    def __init__(self):
        
        self.rotate = []
        self.P = None
        self.cycle_to_tuple = {}
        self.cycle_to_action = {}
        
        AlternatingGroup.__init__(self, 4)
        

        p1 = (0, 0, 1)
        p2 = (.72314028, 0, -.022674794) 
        p3 = (-.3615701, -.626257854, -.022674784)
        p4 = (-.3615701, .626257854, -.022672784)

        self.vertices = (p1, p2, p3, p4)
          
    def geo(self):
        """
        Creates a Graphics3d object of a tetrahedron of height sqrt(3) centered
        at the origin.
        Examples:
	    sage: T = sage.groups.perm_gps.permgroup_named.TetrahedralGroup()         
            sage: T.geo()
        """ 

        from sage.misc.prandom import random
        from sage.plot.plot3d import shapes2, parametric_plot3d
       
        self.P += shapes2.point3d(self.vertices[0], size = 15, color = 255*(random(), random(), random()))
        self.P += shapes2.point3d(self.vertices[1], size = 15, color = 255*(random(), random(), random()))
        self.P += shapes2.point3d(self.vertices[2], size = 15, color = 255*(random(), random(), random()))
        self.P += shapes2.point3d(self.vertices[3], size = 15, color = 255*(random(), random(), random()))

        self.P += shapes2.line3d([self.vertices[0],self.vertices[1]], thickness = 6)
        self.P += shapes2.line3d([self.vertices[0],self.vertices[2]], thickness = 6)
        self.P += shapes2.line3d([self.vertices[0],self.vertices[3]], thickness = 6)
        self.P += shapes2.line3d([self.vertices[1],self.vertices[2]], thickness = 6)
        self.P += shapes2.line3d([self.vertices[1],self.vertices[3]], thickness = 6)
        self.P += shapes2.line3d([self.vertices[2],self.vertices[3]], thickness = 6)
        
        return self.P 

    def symbolic(self):
        raise NotImplementedError

    def getAction(self, element):
        """
        Returns a string describing the cycle's action.
        If no elemeent given, returns the dictionary mapping.
        Not that this should give the correct number of degrees, but due to 
        a bug the axis it defines is not always correct.  See note in build() funciton.
        Examples:
          sage: T = sage.groups.perm_gps.permgroup_named.TetrahedralGroup()
          sage: l = T.list()
          sage: T.getAction(l[2])
            'Rotation by 240 degrees about a vertex (0, 0, 1)'
        """        
       
        if len(self.cycle_to_action)==0:
             self.build()        
        if element is None:
            return self.cycle_to_action
        else:
            return self.cycle_to_action[element]


    def build(self):
        """
        Builds the dictionaries for the Tetrahedral group mapping cycle_to_tuple and
        cycle_to_tuple.  Known bug:  The string returned from the getAction method
        does not always match the axis that the model actually rotates around.  The degree
        of the elements and their matching to the correct type of rotations is correct, 
        but the correct axis is not always given.  This is hard to detect, as no label is 
        associated with the points of the model, but were this to ever change or more functionality
        were added, this would certainly need to be fixed.  It is not hard to fix, it is just a matter
        of inputing the points in a different order into the mappings.  

        Example:
            sage:  T = sage.groups.perm_gps.permgroup_named.TetrahedralGroup()
            sage:  T.build()

        """
        gens = self.gens()

        # The four 3-order elements:
        c0 = gens[0]            # Fixes 3
        c1 = gens[1]            # Fixes 0
        c2 = (gens[0]**2)*gens[1]  # Fixes 2
        c3 = gens[1]*(gens[0]**2)  # Fixes 1

        p0 = self.vertices[0]
        p1 = self.vertices[1]
        p2 = self.vertices[2]
        p3 = self.vertices[3]
        
        # Identity
        self.cycle_to_action[c0**3] = 'Rotation by %s degrees about a vertex (%s, %s, %s)' % (0, p3[0], p3[1], p3[2])
        self.cycle_to_tuple[c0**3] = (0, 0, 1, 0, 20)
        
        # build vertex rotation mappings
        for i in range(1, 3):
            degrees = 120*i
            self.cycle_to_action[c0**i] = 'Rotation by %s degrees about a vertex (%s, %s, %s)' % (degrees, p3[0], p3[1], p3[2])
            self.cycle_to_action[c1**i] = 'Rotation by %s degrees about a vertex (%s, %s, %s)' % (degrees, p0[0], p0[1], p0[2])
            self.cycle_to_action[c2**i] = 'Rotation by %s degrees about a vertex (%s, %s, %s)' % (degrees, p2[0], p2[1], p2[2])
            self.cycle_to_action[c3**i] = 'Rotation by %s degrees about a vertex (%s, %s, %s)' % (degrees, p1[0], p1[1], p1[2])

            self.cycle_to_tuple[c0**i] = (p3[0], p3[1], p3[2], degrees, 20) 
            self.cycle_to_tuple[c1**i] = (p0[0], p0[1], p0[2], degrees, 20)
            self.cycle_to_tuple[c2**i] = (p2[0], p2[1], p2[2], degrees, 20)
            self.cycle_to_tuple[c3**i] = (p1[0], p1[1], p1[2], degrees, 20)
            
        # build elements of order 2
        self.cycle_to_action[c0*c1] = 'Rotation by 180 degrees about the midpoint of (%s, %s, %s) and (%s, %s, %s) and the opposite sides midpoint' % (p0[0], p0[1], p0[2], p2[0], p2[1], p2[2])   # Between c0, c1
        self.cycle_to_action[c1*c0] = 'Rotation by 180 degrees about the midpoint of (%s, %s, %s) and (%s, %s, %s) and the opposite sides midpoint' %(p0[0], p0[1], p0[2], p1[0], p1[1], p1[2])  #  Between c0, c2
        self.cycle_to_action[(c1**2)*c0*(c1**2)] = 'Rotation by 180 degrees about the midpoint of (%s, %s, %s) and (%s, %s, %s) and the opposite sides midpoint' %(p0[0], p0[1], p0[2], p3[0], p3[1], p3[2])   # Between c0, c3

        # Calculate midpoints:
        mid_1x = (p0[0] + p1[1])/float(2)
        mid_1y = (p0[1] + p1[1])/float(2)
        mid_1z = (p0[2] + p1[2])/float(2)

        mid_2x = (p0[0] + p2[1])/float(2)
        mid_2y = (p0[1] + p2[1])/float(2)
        mid_2z = (p0[2] + p2[2])/float(2)

        mid_3x = (p0[0] + p3[1])/float(2)
        mid_3y = (p0[1] + p3[1])/float(2)
        mid_3z = (p0[2] + p3[2])/float(2)

        self.cycle_to_tuple[c0*c1] = (mid_1x, mid_1y, mid_1z, 180, 20)
        self.cycle_to_tuple[c1*c0] = (mid_2x, mid_2y, mid_2z, 180, 20)
        self.cycle_to_tuple[(c1**2)*c0*(c1**2)] = (mid_3x, mid_3y, mid_3z, 180, 20)

               
 
    def addRotate(self, element):
        """
        Add a rotation to the rotation set after passing an element of the Tetrahedral Group.
        Example:
           sage: T = sage.groups.perm_gps.permgroup_named.TetrahedralGroup()
           sage: T.addRotate(T[2])
        """
        if len(self.cycle_to_tuple)==0:
            self.build()
        self.rotate.append(self.cycle_to_tuple[element]) 
        

class CyclicPermutationGroup(PermutationGroup_generic, VisualizeGeo):
    """
    A cyclic group of order n, as a permutation group.

    INPUT:
        n -- a positive integer

    EXAMPLE:
        sage: G = CyclicPermutationGroup(8)
        sage: G.order()
        8
        sage: G
        Cyclic group of order 8 as a permutation group
        sage: loads(G.dumps()) == G
        True
        sage: C = CyclicPermutationGroup(10)
        sage: C.is_abelian()
        True
        sage: C = CyclicPermutationGroup(10)
        sage: C.as_AbelianGroup()
        Multiplicative Abelian Group isomorphic to C2 x C5
        
    """  
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic    
    def __init__(self, n):
        self.P = None  # Graphics        
        self.rotate = [] # Graphic rotate commands
        n = Integer(n)
        if n < 1:
            raise ValueError, "n (=%s) must be >= 1"%n
        gens = tuple(range(1, n+1))
        PermutationGroup_generic.__init__(self, [gens], n)
        self.sym_to_cycle = {}
        self.cycle_to_sym = {}

    def _repr_(self):
        return "Cyclic group of order %s as a permutation group"%self.order()

    def is_commutative(self):
        return True

    def is_abelian(self):
        return True
    	
    def as_AbelianGroup(self):
	"""
	Returns the corresponding Abelian Group instance.
	
	EXAMPLES:
	    
	"""
	n = self.order()
        a = list(factor(n))
        invs = [x[0]**x[1] for x in a]
        G = AbelianGroup(len(a),invs)
	return G

    # Return Dictionary mapping generators to symbols
    def symbolic(self):
        """
        Creates two mappings: 
        Cycle notation --> Symbolic notation
        Symbolic notation --> Cycle notation
        Assumes self.list() returns elements in the format (gen, identity, gen^2, ...)
        x is the symbolic generator and is a variable from sage.calculus.calculus
        returns the dictionaries cycle_to_sym and sym_to_cycle
        Note that this only works because is an abelian group.
        This function should probably be in the format of the other subgroups of SO(R, 3)
        namely with cycle_to_action and cycle_to_tuple dictionaries
 
        Examples:
          sage: R = CyclicPermutationGroup(8)
          sage: a, b = R.symbolic()
                a is the cycle notation --> symbolic notation dictionary
                b is the symbolic notation --> cycle notation dictionary
        """

        from sage.calculus import calculus
        x = calculus.var('x')    
        if len(self.sym_to_cycle) == 0:
            n = self.order()
            cycles = self.list()
            self.sym_to_cycle[x] = cycles[1] #generator
            self.cycle_to_sym[cycles[1]] = x #generator
            
            self.sym_to_cycle[x**1] = cycles[1] # need to map generaotr twice 
            self.sym_to_cycle[x**0] = cycles[0] # need to map identity twice
 
            
            self.sym_to_cycle[1] = cycles[0] #identity
            self.cycle_to_sym[cycles[0]] = 1 #identity
            for i in range(2, n):
                self.sym_to_cycle[x**i] = cycles[i]
                self.cycle_to_sym[cycles[i]] = x**i
        return self.cycle_to_sym, self.sym_to_cycle
   
    
    def getAction(self, element):
        """
        Return string telling how this element acts on an n-gon
        May pass either a element in cycle-form or as a power of x.
        If use a power of x, must use powers of x < order of group.
        Examples:
          sage: r = CyclicPermutationGroup(3)
          sage: r.getAction(x^2)

        """
        if len(self.sym_to_cycle)==0:
            self.symbolic() #Initialize dictionaries
        try:
            element = self.sym_to_cycle[element]
        except KeyError:
            pass
        n = self.list().index(element)   #Assumes guaranteed order of list()
        degrees = str(n*360/float(self.order()))
        return 'Rotation by %s degrees around the center of a %s-gon' % (degrees, self.order())

    def addRotate(self, element):
        """
        Add a rotation given by an elements action to the current set of rotations.  Rotations will be shown
        in order that they are input.  element may be either a power of x or a 
        group element in cycle notation. 
        Examples:
            sage: R = CyclicPermutationGroup(8)
            sage: R.addRotate(x)
        """
        if len(self.sym_to_cycle)==0:
            self.symbolic() #Initialize dictionaries
        try:
            element = self.sym_to_cycle[element]
        except KeyError:
            pass
        n = self.list().index(element)  #Assumes guaranteed order of list()
        degrees = str(n*360/float(self.order()))
        r = (0, 0, 1, degrees, 20)
        self.rotate.append(r) 
     
    def geo(self):
        """ 
        Returns the Graphics3d object of the n-gon.  Vertices are randomaly colored.
        
        
        Examples:
            sage:  R = CyclicPermutationGroup(3)
            sage:  show(R.geo())
        """

        from sage.misc.prandom import random
        from sage.plot.plot3d import shapes2
        from sage.calculus import calculus
        from sage.functions import constants
        pi = float(constants.pi)   
        n = self.order()
        if n == 1:
            self.P = shapes2.point3d((0,0,0), size = 15, color = 255*(random(), random(), random()))           
        else:
            xd = 1
            yd = 0
            self.P = shapes2.point3d((xd, yd, 0), size = 15, color = 255*(random(),random(), random()))
            for i in range(1, n):
                prev_xd = xd
                prev_yd = yd
                xd = calculus.cos(2*i*pi/float(n))
                yd = calculus.sin(2*i*pi/float(n))
                self.P += shapes2.point3d((xd, yd, 0), size = 15, color = 255*(random(), random(), random()))
                self.P += shapes2.line3d([(xd, yd, 0), (prev_xd, prev_yd, 0)], thickness = 6)
            self.P += shapes2.line3d([(xd, yd, 0), (1, 0, 0)], thickness = 6)
        return self.P 
        
class KleinFourGroup(PermutationGroup_generic):
    r"""
    The Klein 4 Group, which has order $4$ and exponent $2$, viewed
    as a subgroup of $S_4$.

    OUTPUT:
        -- the Klein 4 group of order 4, as a permutation group of degree 4.

    EXAMPLES:
        sage: G = KleinFourGroup(); G
        The Klein 4 group of order 4, as a permutation group
        sage: list(G)
        [(), (3,4), (1,2), (1,2)(3,4)]        

    AUTHOR:
        -- Bobby Moretti (2006-10)
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self):
        gens = ((1,2),(3,4))
        PermutationGroup_generic.__init__(self, gens, from_group=True)

    def _repr_(self):
        return 'The Klein 4 group of order 4, as a permutation group'
        

class DihedralGroup(PermutationGroup_generic, VisualizeGeo):
    """
    The Dihedral group of order $2n$ for any integer $n\geq 1$.

    INPUT:
        n -- a positive integer

    OUTPUT:
        -- the dihedral group of order 2*n, as a permutation group

    EXAMPLE:
        sage: DihedralGroup(1)
        Dihedral group of order 2 as a permutation group

        sage: DihedralGroup(2)
        Dihedral group of order 4 as a permutation group
        sage: DihedralGroup(2).gens()
        ((1,2), (3,4))

        sage: DihedralGroup(5).gens()
        ((1,2,3,4,5), (1,5)(2,4))
        sage: list(DihedralGroup(5))
        [(), (2,5)(3,4), (1,2)(3,5), (1,2,3,4,5), (1,3)(4,5), (1,3,5,2,4), (1,4)(2,3), (1,4,2,5,3), (1,5,4,3,2), (1,5)(2,4)]

        sage: G = DihedralGroup(6)
        sage: G.order()
        12
        sage: G = DihedralGroup(5)
        sage: G.order()
        10
        sage: G
        Dihedral group of order 10 as a permutation group
        sage: loads(G.dumps()) == G
        True
        sage: G.gens()
        ((1,2,3,4,5), (1,5)(2,4))

        sage: DihedralGroup(0)
        Traceback (most recent call last):
        ...
        ValueError: n must be positive
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n):
        self.P = None  #Graphics objects
        self.rotate = []
        self.cycle_to_action = {}
        self.cycle_to_tuple = {} 
        self.plist = []  #List of points

        n = Integer(n)
        if n <= 0:
            raise ValueError, "n must be positive"

        # the first generator generates the cyclic subgroup of D_n, <(1...n)> in
        # cycle notation
        gen0 = range(1,n+1)

        if n < 1:
            raise ValueError, "n (=%s) must be >= 1"%n

        # D_1 is a subgroup of S_2, we need the cyclic group of order 2
        if n == 1:
            gens = CyclicPermutationGroup(2).gens()
        elif n == 2:
            gens = ((1,2),(3,4))
        else:
            #gens = tuple(gen0.append([(i, n+1-i) for i in range(1, n//2 + 1)]))
            gen1 = [(i, n-i+1) for i in range(1, n//2 +1)]
            gens = tuple([tuple(gen0),tuple(gen1)])
        # send this off to the parent's class __init__()
        PermutationGroup_generic.__init__(self, gens, from_group = True)
        
    def _repr_(self):
        return "Dihedral group of order %s as a permutation group"%self.order()
     

    def geo(self):
        """ 
        Returns the Graphics3d object of the n-gon.  Vertices are randomaly colored.
        Rotations and Moves are not given with this function.  Use the show() funciton.
        Valid for DihedralGroups of order 6 or greater.
        Examples:
            sage:  R = DihedralGroup(3)
            sage:  show(R.geo())
        """

        from sage.misc.prandom import random
        from sage.plot.plot3d import shapes2
        from sage.calculus import calculus
        from sage.functions import constants
        pi = float(constants.pi)   
        n = self.order()/2
        if n < 3:
            raise NotImplementedError
        if n == 1:
            self.P = shapes2.point3d((0,0,0), size = 15, color = 255*(random(), random(), random()))           
        elif self.P == None:
            xd = 1
            yd = 0
            self.P = shapes2.point3d((xd, yd, 0), size = 15, color = 255*(random(),random(), random()))
            self.plist.append((xd, yd, 0))
            for i in range(1, n):
                prev_xd = xd
                prev_yd = yd
                xd = calculus.cos(2*i*pi/float(n))
                yd = calculus.sin(2*i*pi/float(n))
                self.P += shapes2.point3d((xd, yd, 0), size = 15, color = 255*(random(), random(), random()))
                self.plist.append((xd, yd, 0))
                self.P += shapes2.line3d([(xd, yd, 0), (prev_xd, prev_yd, 0)], thickness = 6)
            self.P += shapes2.line3d([(xd, yd, 0), (1, 0, 0)], thickness = 6)
        return self.P 
        
    def addRotate(self, e):
        """
        Add a single rotation to the rotation list, given a cycle-notation 
        element e.
  
        Example:
            sage:  D = DihedralGroup(3)
            sage:  l = D.list()
            sage:  D.addRotate(l[0])
        """

        if len(self.cycle_to_tuple) == 0:
            self.build()  
        self.rotate.append(self.cycle_to_tuple[e])        

 
    def getAction(self, e):
        """
        Returns a string describing how an element, either in symbolic or cycle notation,
        acts on the n-gon.
        Exampels:
            sage: R = DihedralGroup(4)
            sage: l = R.list()
            sage: R.getAction('l[0]')
            'Rotation about the center of a 4-gon by 0.0 degrees'
        """
        if len(self.cycle_to_action)==0:
            self.build()
        return self.cycle_to_action[e]

    def build(self):
        """    
        Initialize the cycle_to_action and cycle_to_tuple dictionaries.  
        No symbolic notation is supported, unlike the cyclic group.
        Note the useful relation:  xy =( x^-1)*y   where x is rotation about the 
        center and y is flipping about an axis
            
              Examples:
              sage:  R = DihedralGroup(9)
              sage:  R.build()
        """ 
       
        n = self.order()/2
        if len(self.plist)==0:  # Ensure plist has been built
            self.geo()
        
        for i in range(0, n):  # Build cyclic subgroup
            e = self.gens()[0]**i
            degrees = i*360/float(n)
            self.cycle_to_action[e] = 'Rotation about the center of a %s-gon by  %s degrees' % (n, degrees)
            self.cycle_to_tuple[e] = (0, 0, 1, degrees, 20)

        # Build with elements of order 2
        if n %2 == 0:   #point-point and side-side axes
            deg = 0
            for i in range(0, n/2):  #Build order 2 elements 
                p = self.plist[i]
                prevp = self.plist[i+1]
                self.cycle_to_action[self.gens()[1]*(self.gens()[0]**deg)] = 'Rotation by 180 degrees about an axis defined by the midpoint of (%s, %s, %s) and (%s, %s, %s) and its opposite side' % (p[0], p[1], p[2], prevp[0], prevp[1], prevp[2])
                               
                midx = (p[0]+prevp[0])/float(2)
                midy = (p[1]+prevp[1])/float(2)
                midz = (p[2]+prevp[2])/float(2)
                self.cycle_to_tuple[self.gens()[1]*(self.gens()[0]**deg)] = (midx, midy, midz, 180, 20)
                
                deg += 1

                self.cycle_to_action[self.gens()[1]*(self.gens()[0]**deg)] = 'Rotation by 180 degrees about an axis defined by a point (%s, %s, %s) and its opposite point' % (p[0], p[1], p[2])
                self.cycle_to_tuple[self.gens()[1]*(self.gens()[0]**deg)] = (p[0], p[1], p[2], 180, 20)               
                
                deg += 1

        else:    #Rotations are all about a point-side axis
            for i in range(0, n):
                p = self.plist[i]
                e = self.gens()[1]*(self.gens()[0]**i)
                self.cycle_to_action[e] = 'Rotation by 180 degrees about an axis defined by a point (%s, %s, %s) and its opposite side' % (p[0], p[1], p[2])
                self.cycle_to_tuple[e] = (p[0], p[1], p[2], 180, 20)
                



class MathieuGroup(PermutationGroup_generic):
    """
    The Mathieu group of degree $n$.

    INPUT:
        n -- a positive integer in  {9, 10, 11, 12, 21, 22, 23, 24}.

    OUTPUT:
        -- the Mathieu group of degree n, as a permutation group

    EXAMPLE:
        sage: G = MathieuGroup(12)
        sage: G
        Mathieu group of degree 12 and order 95040 as a permutation group
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n):
        n = Integer(n)
        self._n = n
        if not(n in [9, 10, 11, 12, 21, 22, 23, 24]):
            raise ValueError,"argument must belong to {9, 10, 11, 12, 21, 22, 23, 24}."
        id = 'MathieuGroup(%s)'%n
        PermutationGroup_generic.__init__(self, id, from_group=True, check=False)
        
    def _repr_(self):
        return "Mathieu group of degree %s and order %s as a permutation group"%(self._n,self.order())
    
class TransitiveGroup(PermutationGroup_generic):
    """
    The transitive group from the GAP tables of transitive groups.
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, d, n):
        """
        INPUT:
            d -- positive integer; the degree
            n -- positive integer; the number

        OUTPUT:
            the n-th transitive group of degree d

        EXAMPLE:
            sage: G = TransitiveGroup(1,1); G
            Transitive group number 1 of degree 1
            sage: G = TransitiveGroup(5, 2); G         # requires optional database_gap 
            Transitive group number 2 of degree 5
            sage: G.gens()                             # requires optional database_gap 
            ((1,2,3,4,5), (1,4)(2,3))
            
            sage: loads(G.dumps()) == G                # requires optional database_gap 
            True
        """
        if d == 1:
            id = 'Group([()])'
        else:
            id = 'TransitiveGroup(%s,%s)'%(d,n)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._d = d
        self._n = n
        
    def _repr_(self):
        return "Transitive group number %s of degree %s"%(self._n, self._d)

class PGL(PermutationGroup_generic):
    """
    The projective general linear groups over GF(q).
    """
    def __init__(self, n, q, name='a'):
        """
        INPUT:
            n -- positive integer; the degree
            q -- prime power; the size of the ground field
            name -- (default: 'a') variable name of indeterminate of finite field GF(q)
            
        OUTPUT:
            PGL(n,q)

        EXAMPLE:
            sage: G = PGL(2,3); G
            Permutation Group with generators [(3,4), (1,2,4)]
            sage: print G
            The projective general linear group of degree 2 over Finite Field of size 3
            sage: G.base_ring()
            Finite Field of size 3
            sage: G.order()
            24

            
            sage: G = PGL(2, 9, 'b'); G
            Permutation Group with generators [(3,10,9,8,4,7,6,5), (1,2,4)(5,6,8)(7,9,10)]
            sage: G.base_ring()
            Finite Field in b of size 3^2           
        """
        from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
        if n == 1:
            id = 'Group([()])'
        else:
            id = 'PGL(%s,%s)'%(n,q)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        self._n = n
        
    def base_ring(self):
        return self._base_ring

    def __str__(self):
        return "The projective general linear group of degree %s over %s"%(self._n, self.base_ring())

class PSL(PermutationGroup_generic):
    """
    The projective special linear groups over GF(q).
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n, q, name='a'):
        """
        INPUT:
            n -- positive integer; the degree
            q -- prime power; the size of the ground field
            name -- (default: 'a') variable name of indeterminate of finite field GF(q)
            
        OUTPUT:
            PSL(n,q)

        EXAMPLE:
            sage: G = PSL(2,3); G
            Permutation Group with generators [(2,3,4), (1,2)(3,4)]
            sage: G.order()
            12
            sage: G.base_ring()
            Finite Field of size 3
            sage: print G
            The projective special linear group of degree 2 over Finite Field of size 3

        We create two groups over nontrivial finite fields:
            sage: G = PSL(2, 4, 'b'); G
            Permutation Group with generators [(3,4,5), (1,2,3)]
            sage: G.base_ring()
            Finite Field in b of size 2^2
            sage: G = PSL(2, 8); G
            Permutation Group with generators [(3,8,6,4,9,7,5), (1,2,3)(4,7,5)(6,9,8)]
            sage: G.base_ring()
            Finite Field in a of size 2^3
            
        """
        if n == 1:
            id = 'Group([()])'
        else:
            id = 'PSL(%s,%s)'%(n,q)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        self._n = n

    def matrix_degree(self):
        return self._n
    
    def base_ring(self):
        return self._base_ring

    def __str__(self):
        return "The projective special linear group of degree %s over %s"%(self._n, self.base_ring())

    def ramification_module_decomposition_hurwitz_curve(self):
        """
        Helps compute the decomposition of the ramification module
        for the Hurwitz curves X (over CC say) with automorphism group
        G = PSL(2,q), q a "Hurwitz prime" (ie, p is $\pm 1 \pmod 7$).
        Using this computation and Borne's formula helps determine the
        G-module structure of the RR spaces of equivariant
        divisors can be determined explicitly.

        The output is a list of integer multiplicities: [m1,...,mn],
        where n is the number of conj classes of G=PSL(2,p) and mi is the
        multiplicity of pi_i in the ramification module of a
        Hurwitz curve with automorphism group G. 
        Here IrrRepns(G) = [pi_1,...,pi_n] (in the order listed in the
        output of self.character_table()).
        
        REFERENCE: David Joyner, Amy Ksir, Roger Vogeler,
                   "Group representations on Riemann-Roch spaces of some
                   Hurwitz curves," preprint, 2006.

        EXAMPLES:
            sage: G = PSL(2,13)
            sage: G.ramification_module_decomposition_hurwitz_curve() #random
            [0, 7, 7, 12, 12, 12, 13, 15, 14]

        This means, for example, that the trivial representation does not
        occur in the ramification module of a Hurwitz curve with automorphism
        group PSL(2,13), since the trivial representation is listed first
        and that entry has multiplicity 0. The "randomness" is due to the
        fact that GAP randomly orders the conjugacy classes of the same order
        in the list of all conjugacy classes. Similarly, there is some
        randomness to the ordering of the characters.
        
        If you try to use this function on a group PSL(2,q) where q is
        not a (smallish) "Hurwitz prime", an error mesage will be printed. 
        """
        if self.matrix_degree()!=2:
            return ValueError, "Degree must be 2."
        F = self.base_ring()
        q = F.order()           
        from sage.misc.misc import SAGE_EXTCODE
        gapcode = SAGE_EXTCODE + '/gap/joyner/hurwitz_crv_rr_sp.gap'
        gap.eval('Read("'+gapcode+'")')
        mults = gap.eval("ram_module_hurwitz("+str(q)+")")
        return eval(mults)

    def ramification_module_decomposition_modular_curve(self):
        """
        Helps compute the decomposition of the ramification module
        for the modular curve X(p) (over CC say) with automorphism group G = PSL(2,q),
        q a prime > 5. Using this computation and Borne's formula helps determine the
        G-module structure of the RR spaces of equivariant
        divisors can be determined explicitly.

        The output is a list of integer multiplicities: [m1,...,mn],
        where n is the number of conj classes of G=PSL(2,p) and mi is the
        multiplicity of pi_i in the ramification module of a
        modular curve with automorphism group G. 
        Here IrrRepns(G) = [pi_1,...,pi_n] (in the order listed in the
        output of self.character_table()).
        
        REFERENCE: D. Joyner and A. Ksir, 'Modular representations 
                   on some Riemann-Roch spaces of modular curves
                   $X(N)$', Computational Aspects of Algebraic Curves, 
                   (Editor: T. Shaska) Lecture Notes in Computing, WorldScientific,
                   2005.)

        EXAMPLES:
            sage: G = PSL(2,7)
            sage: G.ramification_module_decomposition_modular_curve() ## random
            [0, 4, 3, 6, 7, 8]

        This means, for example, that the trivial representation does not
        occur in the ramification module of X(7), since the trivial representation
        is listed first and that entry has multiplicity 0. The "randomness" is due to the
        fact that GAP randomly orders the conjugacy classes of the same order
        in the list of all conjugacy classes. Similarly, there is some
        randomness to the ordering of the characters.
        """
        if self.matrix_degree()!=2:
            return ValueError, "Degree must be 2."
        F = self.base_ring()
        q = F.order()           
        from sage.misc.misc import SAGE_EXTCODE
        gapcode = SAGE_EXTCODE + '/gap/joyner/modular_crv_rr_sp.gap'
        gap.eval('Read("'+gapcode+'")')
        mults = gap.eval("ram_module_X("+str(q)+")")
        return eval(mults)
    
class PSp(PermutationGroup_generic):
    """
    The projective symplectic linear groups over GF(q).
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n, q, name='a'):
        """
        INPUT:
            n -- positive integer; the degree
            q -- prime power; the size of the ground field
            name -- (default: 'a') variable name of indeterminate of finite field GF(q)
            
        OUTPUT:
            PSp(n,q)

        EXAMPLE:
            sage: G = PSp(2,3); G
            Permutation Group with generators [(2,3,4), (1,2)(3,4)]
            sage: G.order()        
            12                       
            sage: G = PSp(4,3); G
            Permutation Group with generators [(3,4)(6,7)(9,10)(12,13)(17,20)(18,21)(19,22)(23,32)(24,33)(25,34)(26,38)(27,39)(28,40)(29,35)(30,36)(31,37), (1,5,14,17,27,22,19,36,3)(2,6,32)(4,7,23,20,37,13,16,26,40)(8,24,29,30,39,10,33,11,34)(9,15,35)(12,25,38)(21,28,31)]
            sage: G.order()
            25920
            sage: print G
            The projective symplectic linear group of degree 4 over Finite Field of size 3
            sage: G.base_ring()
            Finite Field of size 3

            sage: G = PSp(2, 8, name='alpha'); G
            Permutation Group with generators [(3,8,6,4,9,7,5), (1,2,3)(4,7,5)(6,9,8)]
            sage: G.base_ring()
            Finite Field in alpha of size 2^3
        """
        if n%2 == 1:
            raise TypeError, "The degree n must be even"
        else:
            id = 'PSp(%s,%s)'%(n,q)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        self._n = n
        
    def base_ring(self):
        return self._base_ring

    def __str__(self):
        return "The projective symplectic linear group of degree %s over %s"%(self._n, self.base_ring())

PSP = PSp

class PSU(PermutationGroup_generic):
    """
    The projective special unitary groups over GF(q).

    INPUT:
        n -- positive integer; the degree
        q -- prime power; the size of the ground field
        name -- (default: 'a') variable name of indeterminate of finite field GF(q)
    OUTPUT:
        PSU(n,q)

    EXAMPLE:
        sage: PSU(2,3)
        The projective special unitary group of degree 2 over Finite Field of size 3

        sage: G = PSU(2, 8, name='alpha'); G
        The projective special unitary group of degree 2 over Finite Field in alpha of size 2^3
        sage: G.base_ring()
        Finite Field in alpha of size 2^3
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n, q, name='a'):
        id = 'PSU(%s,%s)'%(n,q)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        self._field_of_definition = GF(q**2, name)
        self._n = n
        
    def field_of_definition(self):
        return self._field_of_definition

    def base_ring(self):
        return self._base_ring

    def _repr_(self):
        return "The projective special unitary group of degree %s over %s"%(self._n, self.base_ring())

class PGU(PermutationGroup_generic):
    """
    The projective general unitary groups over GF(q).

    INPUT:
        n -- positive integer; the degree
        q -- prime power; the size of the ground field
        name -- (default: 'a') variable name of indeterminate of finite field GF(q)
        
    OUTPUT:
        PGU(n,q)

    EXAMPLE:
        sage: PGU(2,3)
        The projective general unitary group of degree 2 over Finite Field of size 3

        sage: G = PGU(2, 8, name='alpha'); G
        The projective general unitary group of degree 2 over Finite Field in alpha of size 2^3
        sage: G.base_ring()
        Finite Field in alpha of size 2^3
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, n, q, name='a'):
        id = 'PGU(%s,%s)'%(n,q)
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        self._field_of_definition = GF(q**2, name)
        self._n = n
        
    def field_of_definition(self):
        return self._field_of_definition

    def base_ring(self):
        return self._base_ring

    def _repr_(self):
        return "The projective general unitary group of degree %s over %s"%(self._n, self.base_ring())


class SuzukiGroup(PermutationGroup_generic):
    r"""
    The Suzuki group over GF(q), $^2 B_2(2^{2k+1}) = Sz(2^{2k+1})$. A wrapper for the GAP function SuzukiGroup.

    INPUT:
        q -- 2^n, an odd power of 2; the size of the ground
             field. (Strictly speaking, n should be greater than 1, or
             else this group os not simple.)
        name -- (default: 'a') variable name of indeterminate of
                finite field GF(q)
        
    OUTPUT:
        A Suzuki group.

    EXAMPLE:
        sage: SuzukiGroup(8)
        Permutation Group with generators [(1,28,10,44)(3,50,11,42)(4,43,53,64)(5,9,39,52)(6,36,63,13)(7,51,60,57)(8,33,37,16)(12,24,55,29)(14,30,48,47)(15,19,61,54)(17,59,22,62)(18,23,34,31)(20,38,49,25)(21,26,45,58)(27,32,41,65)(35,46,40,56), (1,2)(3,10)(4,42)(5,18)(6,50)(7,26)(8,58)(9,34)(12,28)(13,45)(14,44)(15,23)(16,31)(17,21)(19,39)(20,38)(22,25)(24,61)(27,60)(29,65)(30,55)(32,33)(35,52)(36,49)(37,59)(40,54)(41,62)(43,53)(46,48)(47,56)(51,63)(57,64)]
        sage: print SuzukiGroup(8)
        The Suzuki group over Finite Field in a of size 2^3

        sage: G = SuzukiGroup(32, name='alpha')
        sage: G.order()
        32537600
        sage: G.order().factor()
        2^10 * 5^2 * 31 * 41
        sage: G.base_ring()
        Finite Field in alpha of size 2^5
	
    REFERENCES:
        http://en.wikipedia.org/wiki/Group_of_Lie_type\#Suzuki-Ree_groups
    """
    from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
    def __init__(self, q, name='a'):
        q = Integer(q)
        from sage.rings.arith import valuation
        t = valuation(q, 2)
        if 2**t != q or is_even(t):
	    raise ValueError,"The ground field size %s must be an odd power of 2."%q 
        id = 'SuzukiGroup(IsPermGroup,%s)'%q
        PermutationGroup_generic.__init__(self, id, 
                                          from_group=True, check=False)
        self._q = q
        self._base_ring = GF(q, name=name)
        
    def base_ring(self):
        return self._base_ring

    def __str__(self):
        return "The Suzuki group over %s"%self.base_ring()