(*written by David Milne*)

BeginPackage["Groups`"]


(* USAGE DESCRIPTIONS *)
M             ::usage = "M[a1,...,an] is the permutation mapping 1 to a1, ..., n to an."
P             ::usage = "P[{a1,...,am},{b1,...,bn},...] is a permutation in cycle form."
L             ::usage = "L[{a11,...,a1n},...{an1,...,ann}] is a matrix over Z/p, where " <>
                        "p is the current prime chosen (see ChoosePrime)."

Dot           ::usage = "Dot[a,b], or a.b, where a and b are matrices or permutations (of the " <>
                        "same type), returns the permutation mapping i to b(a(i)). " <>
                        "Dot[X,Y], where X and Y are lists of permutations, returns " <>
                        "all products a.b as a ranges over X and b over Y.  " <>
                        "Dot[a,G], or a.G, where a is a permutation and G is a group " <>
                        "returns the list of elements of the left coset.  Similarly, G.a " <>
                        "returns the right coset. "
Inverse       ::usage = "Inverse[a] is the inverse of the permutation or matrix a."
Degree        ::usage = "Degree[a] returns the degree of the matrix or permutation " <>
                        "(in mapping form) a. Degree[G] returns the degree of the group G. "
Order         ::usage = "Order[a] returns the order of the matrix or permutation a, i.e., " <>
                        "the smallest positive integer such that a^n is the identity. " <>
                        "Order[G] returns the number of elements in the group G."
EvenQ         ::usage = "EvenQ[a], where a is a permutation returns true if a is even."
Identity      ::usage = "Identity[a] returns the identity element of the same type as a. "<>
                        "Identity[G] returns the identity subgroup of the group G."
IdentityQ     ::usage = "IdentityQ[a] returns true if a is an identity element."
Elements      ::usage = "Elements[G], where G is a group, returns the list of elements in G."
Generators    ::usage = "Generators[G], where G is a group, returns the list of " <>
                        "generators defining G."

Group         ::usage = "Group[a,b,...], where a, b, ... are elements, all of the " <>
                        "same type, returns the group generated by a, b, ... ."
ExtendGroup   ::usage = "ExtendGroup[G,a] returns the group generated by a and the generators " <>
                        "of G.  If a is already in G, and hence is redundant, a " <>
                        "is not added to the list of generators."
ElementQ      ::usage = "ElementQ[G,a] returns true if a is an element of the group G."

SubgroupQ     ::usage = "SubgroupQ[G,H] returns true if H is a subgroup of G."
NormalizesQ   ::usage = "NormalizesQ[G,a] returns true if a normalizes G, i.e., a.G == G.a."

CentralizesQ  ::usage = "CentralizesQ[G,a] returns true if a commutes with every element of G."
NormalQ       ::usage = "NormalQ[G,H] returns true is H is a normal subgroup of G."
CentralQ      ::usage = "CentralQ[G,H] returns true if H is in the centre of G."

Normalizer    ::usage = "Normalizer[G,H] returns the subgroup of G that normalizes H."
Centralizer   ::usage = "Centralizer[G,H] returns the subgroup of G commuting with H."
Stabilizer    ::usage = "Stabilizer[G,i] returns the subgroup of G fixing i."
Centre        ::usage = "Centre[G] returns the centre of the group G."

NumberOfConjugates ::usage = "NumberOfConjugates[G,H] returns the number of conjugates " <>
                        "of the subgroup H of G."

Conjugate     ::usage = "Conjugate[a,b] returns a.b.Inverse[a]."
ConjugacyClass::usage = "ConjugacyClass[G,a] returns the conjugates of a by elements of G."
Commutator    ::usage = "Commutator[a,b] returns a.b.Inverse[a].Inverse[b]."

LeftCosets    ::usage = ""
LeftCosetReps ::usage = ""
RightCosets   ::usage = ""
RightCosetReps::usage = ""

Orbit         ::usage = "Orbit[a,x], where a is a permutation and x is an integer, " <>
                        "or a is a matrix and x is a vector, returns the orbit of x under a."

ToP           ::usage = "ToP[a] takes a perm in mapping form to one in cycle form."
ToM           ::usage = "ToM[a,n] takes a perm in cycle form to one in mapping perm of degree n."
ToL           ::usage = "ToL[a] takes a mapping perm to a matrix of the corresponding degree."
ToF           ::usage = "ToF[a] takes a 2x2 matrix to the corresponding fractional linear " <>
                        " transformation."
FLTPermutation::usage = "FLTPermutation[a] takes a 2x2 matrix to the corresponding fractional " <>
                        "linear transformation."

CycleType     ::usage = "CycleType[a] returns a list of lengths of cycles of a."
CycleTypes    ::usage = "CycleTypes[G] returns a table of frequencies of cycle types in G." 
ChoosePrime   ::usage = "ChoosePrime[p] sets p to be the current ambient prime."
CurrentPrime  ::usage = "CurrentPrime[] returns the current ambient prime."
FieldChar     ::usage = "FieldChar[a] returns the characteristic of the field of a." 

SubsetQ       ::usage = ""

(* FUNCTION DEFINITIONS BEGIN ------------------------------------------------------------ *)

Begin["`Private`"]


(* FUNCTIONS CONFLICTING WITH OR AUGMENTING BUILT-INS *)

    protected = Unprotect[ Dot, Inverse, Order, Identity, Conjugate, Degree, EvenQ ]


(* SETTING THE AMBIENT PRIME FOR LINEAR CALCULATIONS *)

    globalPrime = 2 
    ChoosePrime[ p_Integer?Positive ] := (globalPrime = p)
    CurrentPrime[] := globalPrime


(* NAMES (HEADS) OF GROUP ELEMENTS IMPLEMENTED IN THIS PACKAGE *)

    ElementTypes = { M, P, L, F }


(* INPUT OF ELEMENTS *)

    P[ cs__List ] := P @@ ( CycleListAction[{cs},#]& /@ Range[ Max @@ Union[cs] ] )
    
    L[ a__List ] := L[ a, globalPrime ]
    L[ a__List, p_Integer ] /;  Min[a] < 0 || Max[a] >= p := L[ Sequence@@Mod[{a},p], p ]


(* OUTPUT OF ELEMENTS *)

    Format[ a_M ] := MatrixForm[ { Range[Degree[a]], List@@a } ]

    Format[ P[] ] := "(1)"
    Format[ a_P ] := 
        StringReplace[ ToString[#], { "{"->"(", "}" ->")", ","->"" } ] & /@ Cycles[a] // 
            StringReplace[ ToString[#], { "{"->"", "}"->"", ", "->"" } ] &
    Format[ a_L ] := MatrixForm[ Matrix[a] ]
    Format[ a_F ] := With[{p=FieldChar[a]},
        StringReplace[ ToString[ ToP[M@@a] ], { ToString[p]->"0", ToString[p+1]->"\[Infinity]" } ] ]
                
    Format[ v_V ] := MatrixForm[ Vector[v] ]


(* CONVERSION BETWEEN ELEMENTS *)

    ToP[ a_M ] := P @@ Collapse[ List@@a ]

    ToM[ P[] ] := M[1]
    ToM[ a_P ] := M @@ a
    ToM[ a_P, n_Integer ] /; ( n >= MinDegree[a] ) := M @@ Array[ a, n ]        

    ToL[ a_P, n_Integer, p_:globalPrime ] := ToL[ ToM[a,n], p ]
    ToL[ a_M, p_:globalPrime ] := 
        L @@ Append[ Transpose[ Array[ BasisVector[ Degree[a], a[#] ]&, Degree[a] ] ], p ]
    BasisVector[ n_, i_ ] := Array[ If[ #==i, 1, 0 ]&, n ]

    ToF[ a_L ] /; Degree[a]==2 := With[{p=FieldChar[a]}, F@@
       Apply[ Function[{x,y}, If[ y==0, p+1, With[{z=Mod[x PowerMod[y,-1,p],p]},If[z==0,p,z]]]]
            , Vector[a.V[#,p]]& /@ Append[ {#,1}& /@ Range[p], {1,0} ]
            , 1 ] ]

    FLTPermutation[a_L] := ToF[a]


(* PREDICATES ON ELEMENTS *)

    GrpEltQ[ a_ ] := MemberQ[ ElementTypes, Head[a] ]
   
    IdentityQ[ a_?GrpEltQ ] := ( a === Identity[a] )
    SameTypeQ[ a_?GrpEltQ, b_?GrpEltQ ] := ( Identity[a] === Identity[b] )
    EvenQ[ a_P | a_M ] := EvenQ[ Plus@@( CycleType[a] - 1 ) ]


(* BASIC INVARIANTS OF ELEMENTS *)

    Degree[ a_M ]    := Length[ a ]
    Degree[ a_L ]    := Length[ First[a] ]
    MinDegree[ a_P ] := Max[ 1, Length[a] ]

    Matrix[ a_L ]    := Drop[List@@a,-1]
    FieldChar[ a_L ] := Last[a]
    FieldChar[ a_F ] := Length[a]-1

    FieldChar[ v_V ] := Last[v]
    Vector[ v_V ]    := First[v]
    Degree[ v_V ]    := Length[Vector[v]]

    Identity[ a_M ] := M @@ Range[ Degree[a] ]
    Identity[ a_P ] := P[]
    Identity[ a_L ] := L @@ Append[ IdentityMatrix[ Degree[a] ], FieldChar[a] ]
    Identity[ a_F ] := F @@ Identity[ M@@a ]

    Order[ a_L | a_M | a_P | a_F ] := OrderAux[a,a]
        OrderAux[ _, an_?IdentityQ ] := 1
        OrderAux[ a_, an_ ]          := 1 + OrderAux[ a, a.an ]

    CycleType[ a_M ] := CycleType[ ToP[a] ]
    CycleType[ P[] ] := {1}
    CycleType[ a_F ] := CycleType[ M@@a ]
    CycleType[ a_P ] := Sort[ Length /@ Cycles[a] ]


(* FUNCTIONS ON ELEMENTS *)

    RaiseDegree[ a_M, n_Integer ] /; n >= Degree[a] :=
        M @@ Join[ List@@a, Range[ Degree[a]+1, n ] ]

    Inverse[ a_M ] := M @@ Last /@ Sort[ Transpose[ { List@@a, Range[ Degree[a] ] } ] ]
    Inverse[ a_P ] := P @@ Inverse[ ToM[a] ] 
    Inverse[ a_L ] := L @@ Append[ Inverse[ Matrix[a], Modulus->FieldChar[a] ], FieldChar[a] ] 
    Inverse[ a_F ] := F @@ Inverse[ M@@a ]

    Cycles[ P[] ] := {}
    Cycles[ a_P ] := Select[ Orbits @@ Transpose[ {Range[MinDegree[a]],List@@a} ], Length[#]>1& ]
    Cycles[ a_M ] := Cycles[ ToP[a] ]


(* FUNCTIONS ON PAIRS OF ELEMENTS *)

    Dot[ a_, b_, cs__ ] := With[ { ab = a.b }, Dot[ab,cs] ]
    Dot[ a_M, b_M ] /; Degree[a]==Degree[b]     := a[[ List@@b ]]
    Dot[ a_P, b_P ] := ToP[ ToM[a,n].ToM[b,n] ]  /.  n -> Max[ MinDegree[a], MinDegree[b]]

    Dot[ a_L, b_L ] /; Identity[a]==Identity[b] := L @@ Append[ Matrix[a].Matrix[b], FieldChar[a] ]
    Dot[ a_F, b_F]  /; FieldChar[a]==FieldChar[b] := F @@ Dot[ M@@a, M@@b ]

    Conjugate[ a_, b_ ]  /; ( GrpEltQ[a] && Identity[a]==Identity[b]) := a.b.Inverse[a]
    Commutator[ a_, b_ ] /; ( GrpEltQ[a] && Identity[a]==Identity[b]) := a.b.Inverse[a].Inverse[b]


(* ACTIONS OF ELEMENTS ON POINTS *)

    a_M[ i_Integer ]  /; ( 0 < i <= Degree[a] )         := a[[i]]
    P[][ i_Integer?Positive ]                           := i
    a_P[ i_Integer ]  /; ( 0 < i <= MinDegree[a] )      := a[[i]]
    a_P[ i_Integer?Positive ]                           := i

    a_L.v:{__Integer}    := a[v]
    a_L[ v:{__Integer} ] := a[ V[ v, FieldChar[a] ] ]
    a_L.v_V              := a[v]
    a_L[ v_V ]           /; Degree[a]==Degree[v] && FieldChar[a]==FieldChar[v] :=
        V[ Mod[ Matrix[a].Vector[v], p ], p ] /. p->FieldChar[a]

    a_F[0]              := a[ FieldChar[a] ]
    a_F[ \[Infinity] ]  := a[ FieldChar[a] +1 ]
    a_F[ i_Integer ]    := With[ {p=FieldChar[a]},
           Switch[ a[[i]], p, 0, p+1, \[Infinity], _, a[[i]] ]      ]

    Orbit[ a_M, i_Integer ]     := OrbitAux[ a, {i} ]
    Orbit[ a_P, i_Integer ]     := OrbitAux[ a, {i} ]
    Orbit[ a_L, v_V ]           := OrbitAux[ a, {v} ]
    Orbit[ a_L, v:{__Integer} ] := Orbit[ a, V[v,FieldChar[a]] ]
    Orbit[ a_F, i_Integer ]     := OrbitAux[ a, {i} ]
        OrbitAux[ a_, xs:{x_,___,x_} ] := Drop[ xs, -1 ]
        OrbitAux[ a_, xs_ ]            := OrbitAux[ a, Append[ xs, a[ Last[xs] ] ] ]


(* SOME AUXILIARY FUNCTIONS (NOT EXPORTED) *)

    CycleAction[ {___,i_,j_,___}, i_ ] := j
    CycleAction[ {j_,___,i_}, i_ ]     := j
    CycleAction[ _, i_ ]               := i

    CycleListAction[ {cs___,c_}, i_ ] := CycleListAction[ {cs}, CycleAction[c,i] ] 
    CycleListAction[ {}, i_ ]         := i

    Orbits[ s1___, {t1___,x_}, s2___, {x_,t2___}, s3___] := Orbits[ s1, {t1,x,t2}, s2, s3 ]
    Orbits[ s1___, {t1___,x_,t2___,x_,t3___}, s2___ ]    := Orbits[ s1, {t1,x,t2,t3}, s2 ]
    Orbits[ s___ ] := List[ s ]

    Closure[ _, {}, B_ ]             := B
    Closure[ f_, A:{x_,___}, B_:{} ] := With[ { Bx = Append[B,x]},
        Closure[ f, ( Union[A,f[x]] ~Complement~ Bx ), Bx ] ]

    Collapse[ l:{ls___,i_} ] /; Length[l] == i := Collapse[ {ls} ]
    Collapse[ l_ ] := l


(* OUTPUT OF GROUPS *)

    Format[ G_Grp ] := AngleBracket @@ Generators[G]


(* BASIC GENERATION OF GROUPS *)

    ToM[ G_Grp, n_Integer ] :=  
        Grp[ ToM[#,n]& /@ Generators[G], ToM[#,n]& /@ Elements[G] ]
    ToP[ G_Grp ]   := Grp[ToP/@Generators[G], ToP/@Elements[G] ]
    Group[ xs__P ] :=  With[ { n = Max @@ (MinDegree /@ {xs}) },
       ToP[ Group @@ ( ToM[#,n]& /@ {xs} ) ] ]
    Group[ x_?GrpEltQ ] /; IdentityQ[x] := Grp[ {x}, {x} ]
    Group[ x_, xs___ ]  /; SameTypeQ[{x,xs}] := 
        Fold[ ExtendGroup, Group[ Identity[x] ], {x,xs} ]

    ExtendGroup[ G_Grp, a_?GrpEltQ ] /; SameTypeQ[G,a] && ElementQ[ G, a ]  := G
    ExtendGroup[ G_Grp, a_?GrpEltQ ] /; SameTypeQ[G,a] && Order[G] == 1     := 
        GenerateGrp[ {a}, {}, {Identity[a]} ]
    ExtendGroup[ G_Grp, a_?GrpEltQ ] /; SameTypeQ[G,a] := 
        GenerateGrp[ Append[Generators[G],a], Elements[G], a.G ]

    GenerateGrp[ X_, XN_, {} ]   := Grp[ X, XN ]
    GenerateGrp[ X_, Xn_, Yn1_ ] := GenerateGrp[ X, #, Complement[X.Yn1,#] ]& @ Union[ Xn, Yn1 ]


(* BASIC GROUP INFORMATION *)

    Generators[ G_Grp ] := First[G]
    Elements[ G_Grp ]   := Last[G]
    Identity[ G_Grp ]   := Identity[ First[ Generators[G] ] ]


(* INVARIANTS OF GROUPS *)

    Order[ G_Grp ]     := Length[ Elements[G] ]
    Type[ G_Grp ]      := Head[ Identity[G] ]
    Degree[ G_Grp ]    /; MemberQ[ {L,M}, Type[G] ] := Degree[ Identity[G] ]
    FieldChar[ G_Grp ] /; Type[G]==L := FieldChar[ Identity[G] ]

    CycleTypes[ G_Grp ] /; Type[G]==M || Type[G]==P :=
        With[ { c = CycleType /@ Elements[G] },
            TableForm[Transpose[{#,Count[c,#]}& /@ Union[c]]] ]


(* GROUP ACTIONS *)

    Orbit[ G_Grp, i_Integer ] /; Type[G]==P :=
        Closure[ Function[ x, (x//#)& /@ Generators[G] ], {i}, {} ]
    Orbit[ G_Grp, i_Integer ] /; Type[G]==M && 0<i<=Degree[G] :=
        Closure[ Function[ x, (x//#)& /@ Generators[G] ], {i}, {} ]
    Orbit[ G_Grp, v_V ]       /; Type[G]==L && Degree[v]==Degree[G] && FieldChar[v]==FieldChar[G] :=
        Closure[ Function[ x, (x//#)& /@ Generators[G] ], {v}, {} ]        
    Orbit[ G_Grp, v:{__Integer} ] := Orbit[ G, V[v,FieldChar[G]] ]
    Orbit[ G_Grp, i_Integer ] /; Type[G]==F :=
        Closure[ Function[ x, (x//#)& /@ Generators[G] ], {i}, {} ]
    Orbit[ G_Grp, \[Infinity] ] /; Type[G]==F :=
        Closure[ Function[ x, (x//#)& /@ Generators[G] ], {\[Infinity]}, {} ]


(* EXTENDING SOME FUNCTIONS TO LISTS AND GROUPS *)

    (*    Inverse cannot be made Listable, because Mathematica represents 
          matrices as lists of lists of scalars, and so a Listable Inverse would 
          thread too deeply.  Instead, we allow the threading only over lists of 
          group elements.       *)
    Inverse[ { a___?GrpEltQ } ] := Inverse /@ {a}

    Conjugate[ a_?GrpEltQ, B:{__?GrpEltQ} ] /; SameTypeQ[Append[B,a]] :=
        Conjugate[a,#]& /@ B

    SubsetQ[ _, {} ]               := True
    SubsetQ[ A:{___}, B:{b_,___} ] := MemberQ[A,b] && SubsetQ[A,Rest[B]]

    Dot[ A:{__?GrpEltQ}, B:{__?GrpEltQ} ] /; SameTypeQ[ Join[A,B] ]   :=
        Union[ Flatten[ Outer[ Dot, A, B ] ] ]
    Dot[ a_?GrpEltQ, B:{__?GrpEltQ} ]     /; SameTypeQ[ Append[B,a] ] := {a}.B
    Dot[ A:{__?GrpEltQ}, b_?GrpEltQ ]     /; SameTypeQ[ Append[A,b] ] := A.{b}
    Dot[ a_?GrpEltQ, G_Grp ]              /; SameTypeQ[ G, a ]        := a.Elements[G]
    Dot[ G_Grp, a_?GrpEltQ ]              /; SameTypeQ[ G, a ]        := Elements[G].a


(* PREDICATES ON GROUPS *)

    SameTypeQ[ A:{__} ] := 
        With[ { t = Union[ Head/@A ] }, ( Length[t] == 1 ) && MemberQ[ ElementTypes, First[t] ] ]
    SameTypeQ[ G_Grp, a_?GrpEltQ ] := SameTypeQ[ Identity[G], a ]
    SameTypeQ[ G_Grp, H_Grp ]      := SameTypeQ[ Identity[G], Identity[H]  ]

    ElementQ[ G_Grp, a_?GrpEltQ ]  /;  SameTypeQ[G,a]  := MemberQ[ Elements[G], a ]
    SubgroupQ[ G_Grp, H_Grp ]      /;  SameTypeQ[G,H]  := 
        And @@ Evaluate[ ElementQ[G,#]& /@ Generators[H] ]
    
    NormalizesQ[ G_Grp, a_?GrpEltQ ]  /;  SameTypeQ[G,a] := 
        And @@ Evaluate[ ElementQ[ G, Conjugate[a,#]]& /@ Generators[G] ]

    NormalQ[ G_Grp, H_Grp ]  /;  SameTypeQ[G,H] := 
        SubgroupQ[G,H] && And @@ Evaluate[ NormalizesQ[H,#]& /@ Generators[G] ]

    CentralizesQ[ G_Grp, a_?GrpEltQ ]  /;  SameTypeQ[G,a] := 
        And @@ Evaluate[ a.# === #.a & /@ Generators[G] ]

    CentralQ[ G_Grp, H_Grp ]  /;  SameTypeQ[G,H]  := 
        SubgroupQ[G,H] && And @@ Evaluate[ CentralizesQ[H,#]& /@ Generators[G] ]


(* GROUPS CONSTRUCTED USING PREDICATES *)

    PredicateSubgroup[ N_, {}, _ ]                  := N
    PredicateSubgroup[ N_, L:{a_,___}, p_ ] /; p[a] := 
        PredicateSubgroup[ #, Complement[L,Elements[#]], p ]& @ ExtendGroup[N,a]
    PredicateSubgroup[ N_, L:{a_,___}, p_ ] := PredicateSubgroup[ N, Complement[L,a.N], p ] 

    Normalizer[ G_Grp, H_Grp ] /;   Type[G] == P && Type[H] ==P :=
       With[ { n = Max[ MinDegree /@  Union[Generators[G],Generators[H]] ] },
             ToP[ Normalizer[ ToM[G,n], ToM[H,n] ] ]
           ]
    Normalizer[ G_Grp, H_Grp ]  /;  SameTypeQ[G,H] := 
        PredicateSubgroup[ Group[Identity[G]], Elements[G], NormalizesQ[H,#]& ]

    Centralizer[ G_Grp, H_Grp ] /;  SameTypeQ[G,H] := 
        PredicateSubgroup[ Group[Identity[G]], Elements[G], CentralizesQ[H,#]& ]
    Centralizer[ G_Grp, a_?GrpEltQ ] /;  SameTypeQ[G,a] := Centralizer[ G, Group[a] ] 

    Centre[ G_Grp ] := Centralizer[G,G]

    Stabilizer[ G_Grp, i_Integer?Positive ]  /; 
        ( Type[G]==M && i<=Degree[G] )  ||  ( Type[G]==P ) := 
        PredicateSubgroup[ Group[Identity[G]], Elements[G], #[i]==i& ]
    Stabilizer[ G_Grp, i_ ]   /; Type[G]==F := 
        PredicateSubgroup[ Group[Identity[G]], Elements[G], #[i]==i& ]
 
    Stabilizer[ G_Grp, v:{__Integer} ]  := Stabilizer[ G, V[v,FieldChar[G]] ]
    Stabilizer[ G_Grp, v_V ]  /; ( Type[G]==L && Degree[v]==Degree[G] && FieldChar[v]==FieldChar[G] ) := 
        PredicateSubgroup[ Group[Identity[G]], Elements[G], #[v]==v& ]

    NormalClosure[ G_Grp, H_Grp ] /; SameTypeQ[G,H] := 
        If[ newelts == {}, H, Fold[ ExtendGroup, H, newelts ] ] /.
            newelts ->  Complement[ Flatten[ 
                            Outer[ Conjugate, Generators[G], Generators[H] ] ], Elements[H] ]
    CommutatorSubgroup[ G_Grp ] := NormalClosure[ G, G1 ] /.
        G1 -> Group @@ Flatten[ Outer[ Commutator, Generators[G], Generators[G] ] ]


(* COSETS AND CONJUGACY CLASSES *)

    LeftCosets[ G_Grp, H_Grp ] /; SameTypeQ[G,H] && SubgroupQ[ G, H ] := 
        LCAux[ H, Elements[G], {} ]
    LeftCosets[ G_Grp, H_Grp ] := Null /; Message[ Perm::subgroup, H, G ]
        LCAux[ _, {}, cosets_ ]             := cosets
        LCAux[ H_, elts:{g_,___}, cosets_ ] := LCAux[ H, Complement[elts,#], Append[cosets,#] ]& [ g.H ]

    LeftCosetReps[ G_Grp, H_Grp ]   := First /@ LeftCosets[G,H]
    RightCosets[ G_Grp, H_Grp ]    := Map[Inverse,#]& /@ LeftCosets[G,H]
    RightCosetReps[ G_Grp, H_Grp ] := Inverse /@ LeftCosetReps[G,H]

    
    ConjugacyClass[ G_Grp, a_?GrpEltQ ] /; SameTypeQ[G,a] := 
        Closure[ Function[ x, Conjugate[#,x]& /@ Generators[G] ], {a} ]

    NumberOfConjugates[ G_Grp, H_Grp ] /; SubgroupQ[G,H] := Order[G]/Order[Normalizer[G,H]]

(* FUNCTION DEFINITIONS END -------------------------------------------------------- *)

    Protect[ Evaluate[protected] ]
	
End[ ]

EndPackage[ ]









