home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-05-05 | 92.0 KB | 3,253 lines |
- #############################################################################
- ##
- #A group.g GAP library Frank Celler
- ##
- #A @(#)$Id: group.g,v 3.63 1993/02/11 17:50:25 martin Rel $
- ##
- #Y Copyright 1990-1992, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
- ##
- ## This file contains dispatcher and default functions for group functions.
- ##
- ## Namely it contains the functions that construct groups and subgroup
- ## (e.g. 'Group' and 'Subgroup'), the functions that implement domain
- ## functions (e.g. 'GroupOp.Element'), the functions that test if a
- ## group has a certain property (e.g. 'IsPerfect'), the functions that
- ## test properties for subgroups (e.g. 'IsNormal'), the functions that
- ## compute important subgroups (e.g. 'SylowSubgroup'), the functions
- ## that compute series (e.g. 'LowerCentralSeries'), and the functions
- ## that compute other information for subgroups (e.g. 'Index').
- ##
- ## Further functions that can be applied to groups are in 'grpcoset.g',
- ## 'grphomom.g', 'grpprods.g' and 'operatio.g'.
- ##
- #H $Log: group.g,v $
- #H Revision 3.63 1993/02/11 17:50:25 martin
- #H the code to compute the radical was incorrect
- #H
- #H Revision 3.62 1993/02/10 10:11:14 martin
- #H added 'PCore' and 'Radical'
- #H
- #H Revision 3.61 1993/01/18 18:54:52 martin
- #H added character table computation
- #H
- #H Revision 3.60 1993/01/04 11:16:53 fceller
- #H added 'Exponent'
- #H
- #H Revision 3.59 1992/12/16 19:47:27 martin
- #H replaced quoted record names with escaped ones
- #H
- #H Revision 3.58 1992/12/02 08:08:42 fceller
- #H moved 'CompositionSeries' and 'PCentralSeries' to "group.tex"
- #H
- #H Revision 3.57 1992/11/30 18:35:28 fceller
- #H added 'ElementaryAbelianSeries' and 'CompositionSeries'
- #H
- #H Revision 3.56 92/08/10 12:21:05 fceller
- #H fixed 'AgGroup' problem for PQp
- #H
- #H Revision 3.55 1992/06/23 08:56:49 fceller
- #H 'Factorization' should work not only for groups but for all
- #H structures with generators.
- #H
- #H Revision 3.54 1992/06/04 07:04:20 fceller
- #H added 'AgGroup' for PqPresentation
- #H
- #H Revision 3.53 1992/06/01 11:14:12 martin
- #H changed 'Subgroup' to really ignore identities in the generators list
- #H
- #H Revision 3.52 1992/03/27 11:14:51 martin
- #H changed mapping to general mapping and function to mapping
- #H
- #H Revision 3.51 1992/03/12 20:27:10 fceller
- #H fixed a minor bug.
- #H
- #H Revision 3.50 1992/02/12 15:44:23 martin
- #H added the lattice package
- #H
- #H Revision 3.49 1992/02/11 12:44:04 martin
- #H added 'SmallestGenerators'
- #H
- #H Revision 3.48 1992/02/11 12:39:49 martin
- #H improved 'GroupOps.IsSubset' for common cases
- #H
- #H Revision 3.47 1992/02/07 13:36:09 fceller
- #H Initial GAP 3.1 release.
- #H
- #H Revision 3.1 1991/05/06 11:23:50 fceller
- #H Initial revision under RCS.
- ##
-
-
- #############################################################################
- ##
- #F InfoGroup?( ... ) . . . . . . information function for the group package
- ##
- if not IsBound( InfoGroup1 ) then InfoGroup1 := Ignore; fi;
- if not IsBound( InfoGroup2 ) then InfoGroup2 := Ignore; fi;
-
-
- #############################################################################
- ##
- #F GroupString(<G>,<name>) . . . . . . . . . . . . group information string
- ##
- GroupString := function ( G, name )
- if IsBound( G.name ) then
- if IsBound( G.size ) then
- return ConcatenationString(
- G.name, " ",
- "(size ", StringInt(G.size), ")"
- );
- else
- return G.name;
- fi;
- else
- if IsBound( G.size ) then
- return ConcatenationString(
- "<", name, "> ",
- "(", StringInt(Length(G.generators)), " gens, ",
- "size ", StringInt(G.size), ")"
- );
- else
- return ConcatenationString(
- "<", name, "> ",
- "(", StringInt(Length(G.generators)), " gens)"
- );
- fi;
- fi;
- end;
-
-
- #############################################################################
- ##
-
- #V GroupOps . . . . . . . . . . . . . . . . . operations record for groups
- ##
- ## 'GroupOps' is the operations record for groups. This is initially a copy
- ## of 'DomainOps'. This way all the default methods for domains are
- ## inherited.
- ##
- ## The operations records for special groups are created by making a copy of
- ## this record and overlaying some functions. This way those groups inherit
- ## the default methods.
- ##
- GroupOps := Copy( DomainOps );
-
-
- #############################################################################
- ##
-
- #F Group(<gen>,...) or Group(<gens>,<id>) or Group(<D>) . . create a group
- ##
- Group := function ( arg )
- local G, # group containing the elements of <arg>, result
- D, # domain containing the elements of <arg>
- gens, # elements of <arg> as a flat list
- id, # identity element
- i; # loop variable
-
- # if called with one domain argument look in the operations record
- if Length( arg ) = 1 and IsDomain( arg[1] ) then
- G := arg[1].operations.Group( arg[1] );
-
- # special case for matrices, because they look like lists
- elif Length( arg ) = 2 and IsMat( arg[1] ) then
- gens := arg;
- id := arg[1]^0;
- D := Domain( gens );
- G := D.operations.Group( D, gens, id );
-
- # list of generators plus identity
- elif Length( arg ) = 2 and IsList( arg[1] ) then
- gens := arg[1];
- id := arg[2];
- D := Domain( Concatenation( gens, [ id ] ) );
- G := D.operations.Group( D, gens, id );
-
- # generators
- elif 0 < Length( arg ) then
- gens := arg;
- id := arg[1]^0;
- D := Domain( gens );
- G := D.operations.Group( D, gens, id );
-
- # no argument given, error
- else
- Error("usage: Group(<gen>,...) or Group(<gens>,<id>) or Group(<D>)");
- fi;
-
- # return the group
- for i in [ 1 .. Length(G.generators) ] do
- G.(i) := G.generators[i];
- od;
- return G;
-
- end;
-
- GroupElementsOps.Group := function ( GroupElements, gens, id )
- local G; # group containing <gens>, result
-
- # make the domain
- G := rec();
- G.isDomain := true;
- G.isGroup := true;
-
- # enter identification
- G.identity := id;
- if id in gens then
- G.generators := Filtered( gens, gen -> gen <> id );
- else
- G.generators := ShallowCopy( gens );
- fi;
-
- # enter operations record
- G.operations := GroupOps;
-
- # return the group
- return G;
-
- end;
-
-
- #############################################################################
- ##
- #F AsGroup( <D> ) . . . . . . . . . . . . . . . . view a domain as a group
- ##
- AsGroup := function ( D )
- local G;
-
- # check that the argument is a domain or a list
- if not IsDomain( D ) and not IsList( D ) then
- Error( "<D> must be a list or a domain" );
- fi;
-
- # convert a domain into a group
- if IsDomain( D ) then
- G := D.operations.AsGroup( D );
-
- # convert a list into a group
- else
- G := Domain( D ).operations.AsGroup( D );
- fi;
-
- # return the group
- return G;
-
- end;
-
- GroupElementsOps.AsGroup := function ( D )
- local G, L;
-
- # handle trivial case
- if IsGroup( D ) then
- G := ShallowCopy( D );
-
- # otherwise take elements from the domain
- else
- D := Set( D );
- L := ShallowCopy( D );
- G := TrivialSubgroup( Group( D, D[1]^0 ) );
- SubtractSet( L, Elements( G ) );
- while L <> [] do
- G := Closure( G, L[1] );
- SubtractSet( L, Elements( G ) );
- od;
- if Length( Elements( G ) ) <> Length( D ) then
- Error( "the elements of <D> must form a group" );
- fi;
- G := Group( G.generators, D[1]^0 );
- G.elements := D;
- G.isFinite := true;
- G.size := Length( D );
- fi;
-
- # return the group
- return G;
-
- end;
-
- GroupOps.AsGroup := function( G )
- return ShallowCopy( G );
- end;
-
-
- #############################################################################
- ##
- #F IsGroup( <obj> ) . . . . . . . . . . . . . test if an object is a group
- ##
- IsGroup := function ( obj )
- return IsRec( obj )
- and IsBound( obj.isGroup )
- and obj.isGroup;
- end;
-
-
- #############################################################################
- ##
- #F IsParent( <G> ) . . . . . . . . . . . . test if a group is a parent group
- ##
- IsParent := function ( G )
- return G.operations.IsParent( G );
- end;
-
- GroupOps.IsParent := function ( G )
- return not IsBound( G.parent );
- end;
-
-
- #############################################################################
- ##
- #F Parent( <G>, ... ) . . . . . . . . . . . . . . . . . common parent group
- ##
- Parent := function ( arg )
- local G;
-
- # check the number of arguments
- if Length( arg ) = 0 then
- Error( "usage: Parent( <G>, ... )" );
- fi;
-
- # compute the parent group
- G := arg[1].operations.Parent( arg );
-
- # return the parent group
- return G;
-
- end;
-
- GroupOps.Parent := function ( L )
- local G, # parent of first group, result
- H; # loop variable
-
- # get the parent of the first group
- if IsBound( L[1].parent ) then
- G := L[1].parent;
- else
- G := L[1];
- fi;
-
- # check that all other groups have the same parent
- for H in L do
- if IsBound( H.parent ) and H.parent <> G then
- Error( "<G> and <H> must have the same parent group" );
- elif not IsBound( H.parent ) and H <> G then
- Error( "<G> and <H> must have the same parent group" );
- fi;
- od;
-
- # return the parent
- return G;
-
- end;
-
-
- #############################################################################
- ##
- #V MaintainedGroupInfo . . . . . . . . . . . . . . . maintained info fields
- ##
- MaintainedGroupInfo := [
- "isCyclic", "isElementaryAbelian", "isAbelian", "isSolvable",
- "isNilpotent", "isPerfect", "isSimple", "isFinite", "size"
- ];
-
-
- #############################################################################
- ##
-
- #F GroupOps.Group( <D> ) . . . . . . . . . . convert a subgroup into a group
- ##
- GroupOps.Group := function ( D )
- local G, # group for the domain <D>, result
- name; # component name in the group record
-
- # make the group
- G := Group( D.generators, D.identity );
-
- # copy information
- for name in Intersection( RecFields( D ), MaintainedGroupInfo ) do
- G.(name) := D.(name);
- od;
-
- # return the group
- return G;
-
- end;
-
-
- #############################################################################
- ##
- #F GroupOps.Elements( <G> ) . . . . . . . . set of the elements of a group
- ##
- GroupOps.Elements := function ( G )
- local H, # subgroup of the first generators of <G>
- gen; # generator of <G>
-
- # start with the trivial group and its element list
- H := TrivialSubgroup( G );
- H.elements := [ H.identity ];
-
- # add the generators one after the other
- for gen in G.generators do
- H := GroupOps.Closure( H, gen );
- od;
-
- # return the list of elements
- return H.elements;
-
- end;
-
-
- #############################################################################
- ##
- #F GroupOps.\=( <G>, <H> ) . . . . . . . . . test if two groups are equal
- ##
- GroupOps.\= := function ( G, H )
- local isEql;
-
- if IsGroup( G ) and IsFinite( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isEql := G.generators = H.generators
- or IsEqualSet( G.generators, H.generators )
- or ( Size( G ) = Size( H )
- and ForAll( G.generators, gen -> gen in H )
- and ForAll( H.generators, gen -> gen in G ));
- elif IsGroup( H ) then
- isEql := false;
- elif IsCoset( H ) and IsFinite( H ) then
- if G <> H.group then
- isEql := false;
- else
- isEql := H.representative in H.group;
- fi;
- elif IsCoset( H ) then
- isEql := false;
- else
- isEql := DomainOps.\=( G, H );
- fi;
- elif IsGroup( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isEql := false;
- elif IsGroup( H ) then
- isEql := G.generators = H.generators
- or IsEqualSet( G.generators, H.generators )
- or ( ForAll( G.generators, gen -> gen in H )
- and ForAll( H.generators, gen -> gen in G ));
- elif IsCoset( H ) and IsFinite( H ) then
- isEql := false;
- elif IsCoset( H ) then
- if G <> H.group then
- isEql := false;
- else
- isEql := H.representative in H.group;
- fi;
- else
- isEql := DomainOps.\=( G, H );
- fi;
- elif IsCoset( G ) and IsFinite( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- if G.group <> H then
- isEql := false;
- else
- isEql := G.representative in G.group;
- fi;
- elif IsGroup( H ) then
- isEql := false;
- else
- isEql := DomainOps.\=( G, H );
- fi;
- elif IsCoset( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isEql := false;
- elif IsGroup( H ) then
- if G.group <> H then
- isEql := false;
- else
- isEql := G.representative in G.group;
- fi;
- else
- isEql := DomainOps.\=( G, H );
- fi;
- else
- isEql := DomainOps.\=( G, H );
- fi;
- return isEql;
-
- end;
-
-
- #############################################################################
- ##
- #F GroupOps.IsSubset( <G>, <H> ) . . . . . . . . . test for subset of groups
- ##
- GroupOps.IsSubset := function ( G, H )
- local isSub;
-
- if IsGroup( G ) and IsFinite( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isSub := G.generators = H.generators
- or IsSubsetSet( G.generators, H.generators )
- or (IsBound( H.parent ) and G = H.parent)
- or ( Size( G ) >= Size( H )
- and ForAll( H.generators, gen -> gen in G ));
- elif IsGroup( H ) then
- isSub := false;
- elif IsCoset( H ) and IsFinite( H ) then
- isSub := IsSubset( G, H.group )
- and H.representative in G;
- elif IsCoset( H ) then
- isSub := false;
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- elif IsGroup( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isSub := G.generators = H.generators
- or IsSubsetSet( G.generators, H.generators )
- or (IsBound( H.parent ) and G = H.parent)
- or ForAll( H.generators, gen -> gen in G );
- elif IsGroup( H ) then
- isSub := G.generators = H.generators
- or IsSubsetSet( G.generators, H.generators )
- or (IsBound( H.parent ) and G = H.parent)
- or ForAll( H.generators, gen -> gen in G );
- elif IsCoset( H ) and IsFinite( H ) then
- isSub := IsSubset( G, H.group )
- and H.representative in G;
- elif IsCoset( H ) then
- isSub := IsSubset( G, H.group )
- and H.representative in G;
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- elif IsCoset( G ) and IsFinite( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isSub := G.identity in G.group
- and ForAll( H.generators, gen -> gen in G );
- elif IsGroup( H ) then
- isSub := false;
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- elif IsCoset( G ) then
- if IsGroup( H ) and IsFinite( H ) then
- isSub := G.identity in G.group
- and ForAll( H.generators, gen -> gen in G );
- elif IsGroup( H ) then
- isSub := G.identity in G.group
- and ForAll( H.generators, gen -> gen in G );
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- return isSub;
-
- end;
-
-
- #############################################################################
- ##
- #F GroupOps.Intersection( <G>, <H> ) . . . . . . . . intersection of groups
- ##
- GroupOps.Intersection := function ( G, H )
-
- # if one argument is a list, filter this list
- if IsList( G ) then
- return Filtered( G, g -> g in H );
- elif IsList( H ) then
- return Filtered( H, g -> g in G );
- fi;
-
- # if the groups have different parents, use the domain method
- if IsBound( G.parent ) then
- if IsBound( H.parent ) then
- if G.parent <> H.parent then
- return DomainOps.Intersection( G, H );
- fi;
- elif H <> G.parent then
- return DomainOps.Intersection( G, H );
- fi;
- else
- if IsBound( H.parent ) then
- if G <> H.parent then
- return DomainOps.Intersection( G, H );
- fi;
- elif G <> H then
- return DomainOps.Intersection( G, H );
- fi;
- fi;
-
- # construct this group of stabilizer of a right coset
- if not IsFinite( G ) then
- return Stabilizer( H, Coset( G ), OnRight );
- elif not IsFinite( H ) then
- return Stabilizer( G, Coset( H ), OnRight );
- elif Size( G ) < Size( H ) then
- return Stabilizer( G, Coset( H ), OnRight );
- else
- return Stabilizer( H, Coset( G ), OnRight );
- fi;
-
- end;
-
-
- #############################################################################
- ##
- #F GroupOps.Print( <G> ) . . . . . . . . . . . . . . . pretty print a group
- ##
- GroupOps.Print := function ( G )
- local i;
-
- # if the group has a name we use this
- if IsBound( G.name ) then
- Print( G.name );
-
- # if the group is a parent print it as 'Group(...)'
- elif not IsBound( G.parent ) then
-
- # if the group is not trivial the identity need not be printed
- if G.generators <> [] then
- Print( "Group( ");
- for i in [ 1 .. Length(G.generators)-1 ] do
- Print( G.generators[i], ", " );
- od;
- Print( G.generators[Length(G.generators)], " )" );
-
- # if the group is trivial print it as 'Group( <id> )'
- else
- Print( "Group( ", G.identity, " )" );
- fi;
-
- # if the group is a subgroup print it as 'Subgroup(...)'
- else
- Print( "Subgroup( ", G.parent, ", ", G.generators, " )" );
- fi;
-
- end;
-
-
- #############################################################################
- ##
-
- #F Subgroup( <G>, <gens> ) . . . . . . . . . . . . . . . . create a subgroup
- ##
- Subgroup := function ( G, gens )
- local U, # subgroup of <G> with generators <gens>, result
- i; # loop variable
-
- # check the arguments
- if not IsGroup( G ) or not IsParent( G ) then
- Error( "<G> must be a parent group" );
- fi;
- if not ForAll( gens, gen -> gen in G ) then
- Error( "the generators <gens> must lie in <G>" );
- fi;
-
- # make the subgroup
- U := G.operations.Subgroup( G, gens );
- for i in [ 1 .. Length( U.generators ) ] do
- U.(i) := U.generators[i];
- od;
-
- # return the subgroup
- return U;
-
- end;
-
- GroupOps.Subgroup := function ( G, gens )
- local U; # subgroup of <G> generated by <gens>, result
-
- # remove the identity from the list of generators
- if G.identity in gens then
- gens := Filtered( gens, gen -> gen <> G.identity );
- else
- gens := ShallowCopy( gens );
- fi;
-
- # handle special case
- if IsEqualSet( G.generators, gens ) then
- U := G;
-
- # otherwise
- else
-
- # make the subgroup
- U := rec();
- U.isDomain := true;
- U.isGroup := true;
- U.parent := G;
-
- # enter the identification
- U.identity := G.identity;
- U.generators := gens;
-
- # enter the operations record
- U.operations := GroupOps;
-
- fi;
-
- # return the subgroup
- return U;
-
- end;
-
-
- #############################################################################
- ##
- #F AsSubgroup( <G>, <U> ) . . . . view a group as subgroup of another group
- ##
- AsSubgroup := function ( G, U )
-
- # check the arguments
- if not IsGroup( G ) or not IsParent( G ) then
- Error( "<G> must be a parent group" );
- fi;
- if not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
- if not ForAll( U.generators, gen -> gen in G ) then
- Error( "the generators of <U> must lie in <G>" );
- fi;
-
- # dispatch
- return G.operations.AsSubgroup( G, U );
-
- end;
-
- GroupOps.AsSubgroup := function ( G, U )
- return G.operations.Subgroup( G, U.generators );
- end;
-
-
- #############################################################################
- ##
- #F Centralizer( <G>, <obj> ) . . . . centralizer of a subgroup or an element
- ##
- Centralizer := function ( G, obj )
- local C; # centralizer of <obj> in <G>, result
-
- # check the arguments
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( obj ) and not obj in Parent( G ) then
- Error( "<obj> must be an element of the parent of <G>" );
- elif IsGroup( obj ) and Parent( obj ) <> Parent( G ) then
- Error( "<obj> must be a subgroup of the parent of <G>" );
- fi;
- InfoGroup1( "#I Centralizer: of <obj> in ", GroupString(G,"G"), "\n" );
-
- # compute the centralizer
- if IsParent( G ) and IsGroup( obj ) then
- if not IsBound( obj.centralizer ) then
- obj.centralizer := G.operations.Centralizer( G, obj );
- fi;
- C := obj.centralizer;
- else
- C := G.operations.Centralizer( G, obj );
- fi;
-
- # return the centralizer
- InfoGroup1( "#I Centralizer: returns ", GroupString(C,"C"), "\n" );
- return C;
-
- end;
-
- GroupOps.Centralizer := function ( G, obj )
- local C, # centralizer of <obj> in <G>, result
- gen; # one generator of subgroup <obj>
-
- # centralizer of a subgroup
- if IsGroup( obj ) then
- C := G;
- for gen in obj.generators do
- C := Stabilizer( C, gen );
- od;
-
- # centralizer of an element in a subgroup
- else
- C := Stabilizer( G, obj );
- fi;
-
- # return the centralizer
- return C;
-
- end;
-
-
- #############################################################################
- ##
- #F Centre( <G> ) . . . . . . . . . . . . . . . . . . . . . centre of a group
- ##
- Centre := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I Centre: ", GroupString(G,"G"), "\n" );
-
- # compute the centre
- if not IsBound( G.centre ) then
- G.centre := G.operations.Centre( G );
- fi;
-
- # return the centre
- InfoGroup1("#I Centre returns ", GroupString(G.centre,"C"), "\n" );
- return G.centre;
-
- end;
-
- GroupOps.Centre := function ( G )
- local C; # centre of <G>, result
-
- # if <G> is abelian it is its own centre
- if IsBound( G.isAbelian ) and G.isAbelian then
- C := G;
-
- # otherwise compute the centralizer of <G> in <G>
- else
- C := Centralizer( G, G );
- fi;
-
- # return the centre
- return C;
-
- end;
-
-
- #############################################################################
- ##
- #F Closure(<G>,<obj>) . closure of a group with another group or an element
- ##
- Closure := function ( G, obj )
- local C; # closure of <G> with <obj>, result
-
- # check the arguments
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( obj ) and not obj in Parent( G ) then
- Error( "<obj> must be an element of the parent of <G>" );
- elif IsGroup( obj ) and Parent( G ) <> Parent( obj ) then
- Error( "<obj> must be a subgroup of the parent of <G>" );
- fi;
- InfoGroup1( "#I Closure: of ", GroupString(G,"G"), " and <obj>\n" );
-
- # compute the closure
- C := G.operations.Closure( G, obj );
-
- # return the closure
- InfoGroup1( "#I Closure: returns ", GroupString(C,"C"), "\n" );
- return C;
-
- end;
-
- GroupOps.Closure := function ( G, obj )
- local C, # closure '\< <G>, <obj> \>', result
- gen, # generator of <G> or <C>
- reps, # representatives of cosets of <G> in <C>
- rep; # representative of coset of <G> in <C>
-
- # handle the closure of a group with a subgroup
- if IsGroup( obj ) then
- if IsParent( G ) then
- C := G;
- elif IsParent( obj ) then
- C := obj;
- else
- C := G;
- for gen in obj.generators do
- C := G.operations.Closure( C, gen );
- od;
- fi;
-
- # closure of a group with a single element
- else
-
- # try to avoid adding an element to a group that already contains it
- if IsParent( G )
- or obj in G.generators
- or obj^-1 in G.generators
- or (IsBound( G.elements ) and obj in G.elements)
- or obj = G.identity
- then
- return G;
- fi;
-
- # make the closure group
- C := G.operations.Subgroup( Parent(G),
- Concatenation( G.generators, [obj] ) );
-
- # if <G> is nonabelian then so is <C>
- if IsBound( G.isAbelian ) and not G.isAbelian then
- C.isAbelian := false;
- elif IsBound( G.isAbelian ) then
- C.isAbelian := ForAll( G.generators,
- gen -> Comm(gen,obj)=G.identity );
- fi;
-
- # if <G> is infinite then so is <C>
- if IsBound( G.isFinite ) and not G.isFinite then
- C.isFinite := false;
- C.size := "infinity";
- fi;
-
- # if the elements of <G> are known then extend this list
- if IsBound( G.elements ) then
-
- # if <G>^<obj> = <G> then <C> = <G> * <obj>
- if ForAll( G.generators, gen -> gen ^ obj in G.elements ) then
- InfoGroup2( "#I new generator normalizes\n" );
- C.elements := ShallowCopy( G.elements );
- rep := obj;
- while not rep in G.elements do
- Append( C.elements, G.elements * rep );
- rep := rep * obj;
- od;
- C.elements := Set( C.elements );
- C.isFinite := true;
- C.size := Length( C.elements );
-
- # otherwise use a Dimino step
- else
- InfoGroup2( "#I new generator normalizes not\n" );
- C.elements := ShallowCopy( G.elements );
- reps := [ G.identity ];
- InfoGroup2( "\r#I |<cosets>| = ",Length(reps), "\c" );
- for rep in reps do
- for gen in C.generators do
- if not rep * gen in C.elements then
- Append( C.elements, G.elements * (rep * gen) );
- Add( reps, rep * gen );
- InfoGroup2( "\r#I |<cosets>| = ",
- Length(reps),"\c");
- fi;
- od;
- od;
- InfoGroup2( "\n" );
- C.elements := Set( C.elements );
- C.isFinite := true;
- C.size := Length( C.elements );
-
- fi;
- fi;
-
- fi;
-
- # return the closure
- return C;
-
- end;
-
-
- #############################################################################
- ##
- #F CommutatorFactorGroup( <G> ) . . . . commutator factor group of a group
- ##
- CommutatorFactorGroup := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I CommutatorFactorGroup: ", GroupString(G,"G"), "\n" );
-
- # compute the commutator factor group
- if not IsBound( G.commutatorFactorGroup ) then
- G.commutatorFactorGroup := G.operations.CommutatorFactorGroup( G );
- fi;
-
- # return the commutator factor group
- InfoGroup1( "#I CommutatorFactorGroup: ",
- GroupString(G.commutatorFactorGroup,"F"),"\n");
- return G.commutatorFactorGroup;
-
- end;
-
- GroupOps.CommutatorFactorGroup := function ( G )
- return FactorGroup( G, DerivedSubgroup( G ) );
- end;
-
-
- #############################################################################
- ##
- #F CommutatorSubgroup( <U>, <V> ) . . . . commutator subgroup of two groups
- ##
- CommutatorSubgroup := function ( U, V )
- local C;
-
- # check that the arguments are groups with a common parent
- if not IsGroup( U ) then
- Error( "<U> must be a group" );
- elif not IsGroup( V ) then
- Error( "<V> must be a group" );
- fi;
-
- # <U> and <V> must have a common parent
- Parent( U, V );
- InfoGroup1( "#I CommutatorSubgroup: of <U> and <V>\n" );
-
- # compute the commutator subgroup
- C := U.operations.CommutatorSubgroup( U, V );
-
- # return the commutator subgroup
- InfoGroup1( "#I CommutatorSubgroup: ", GroupString(C,"C"), "\n" );
- return C;
-
- end;
-
- GroupOps.CommutatorSubgroup := function ( U, V )
- local C, u, v, c;
-
- # [ <U>, <V> ] = normal closure of < [ <u>, <v> ] >.
- C := TrivialSubgroup( U );
- for u in U.generators do
- for v in V.generators do
- c := Comm( u, v );
- if not c in C then
- C := Closure( C, c );
- fi;
- od;
- od;
- return NormalClosure( Closure( U, V ), C );
-
- end;
-
-
- #############################################################################
- ##
- #F Core( <G>, <U> ) . . . . . . . . . . . . . core of a subgroup in a group
- ##
- Core := function ( G, U )
- local C; # core of <U> in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
- Parent( G, U );
- InfoGroup1( "#I Core: of ", GroupString(U,"U"), " in ",
- GroupString(G,"G"), "\n");
-
- # compute the core
- if IsParent( G ) then
- if not IsBound( U.core ) then
- U.core := G.operations.Core( G, U );
- fi;
- C := U.core;
- else
- C := G.operations.Core( G, U );
- fi;
-
- # return the core
- InfoGroup1( "#I Core: returns ", GroupString(C,"C"), "\n" );
- return C;
-
- end;
-
- GroupOps.Core := function ( G, U )
- local C, # core of <U> in <G>, result
- i; # loop variable
-
- # start with the subgroup <U>
- C := U;
- InfoGroup2("#I Core: approx. is ",GroupString(C,"C"),"\n");
-
- # loop until all generators normalize <C>
- i := 1;
- while i <= Length( G.generators ) do
-
- # if <C> is not normalized by this generator take the intersection
- # with the conjugate subgroup and start all over again
- if not ForAll( C.generators, gen -> gen ^ G.generators[i] in C ) then
- C := Intersection( C, C ^ G.generators[i] );
- InfoGroup2("#I Core: approx. is ",GroupString(C,"C"),"\n");
- i := 1;
-
- # otherwise try the next generator
- else
- i := i + 1;
- fi;
-
- od;
-
- # return the core
- return C;
-
- end;
-
-
- #############################################################################
- ##
- #F DerivedSubgroup( <G> ) . . . . . . . . . . . derived subgroup of a group
- ##
- DerivedSubgroup := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I DerivedSubgroup: ", GroupString(G,"G"), "\n" );
-
- # compute the derived subgroup
- if not IsBound( G.derivedSubgroup ) then
- G.derivedSubgroup := G.operations.DerivedSubgroup( G );
- fi;
-
- # return the derived subgroup
- InfoGroup1( "#I DerivedSubgroup: ",
- GroupString(G.derivedSubgroup,"D"), "\n");
- return G.derivedSubgroup;
-
- end;
-
- GroupOps.DerivedSubgroup := function ( G )
- local D, # derived subgroup of <G>, result
- i, j, # loops
- comm; # commutator of two generators of <G>
-
- # find the subgroup generated by the commutators of the generators
- D := TrivialSubgroup( G );
- for i in [ 2 .. Length( G.generators ) ] do
- for j in [ 1 .. i - 1 ] do
- comm := Comm( G.generators[i], G.generators[j] );
- if not comm in D then
- D := Closure( D, comm );
- fi;
- od;
- od;
-
- # return the normal closure of <D> in <G>
- return NormalClosure( G, D );
-
- end;
-
-
- #############################################################################
- ##
- #F FittingSubgroup( <G> ) . . . . . . . . . . . Fitting subgroup of a group
- ##
- FittingSubgroup := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error("<G> must be a group");
- fi;
- InfoGroup1( "#I FittingSubgroup: ", GroupString(G,"G"), "\n" );
-
- # compute the Fitting subgroup
- if not IsBound( G.fittingSubgroup ) then
- G.fittingSubgroup := G.operations.FittingSubgroup( G );
- fi;
-
- # return the Fitting subgroup
- InfoGroup1( "#I FittingSubgroup: ",
- GroupString(G.fittingSubgroup, "F" ), "\n" );
- return G.fittingSubgroup;
-
- end;
-
- GroupOps.FittingSubgroup := function ( G )
- local F;
-
- if G.generators = [] then
- F := G;
- else
- F := Subgroup( Parent( G ), Union( List( Set( Factors( Size( G ) ) ),
- p -> Core( G, SylowSubgroup(G,p) ).generators ) ) );
- fi;
- return F;
-
- end;
-
-
- #############################################################################
- ##
- #F FrattiniSubgroup( <G> ) . . . . . . . . . . Frattini subgroup of a group
- ##
- FrattiniSubgroup := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I FrattiniSubgroup: ", GroupString(G,"G"), "\n" );
-
- # compute the Frattini subgroup
- if not IsBound( G.frattiniSubgroup ) then
- G.frattiniSubgroup := G.operations.FrattiniSubgroup( G );
- fi;
-
- # return the Frattini subgroup
- InfoGroup1( "#I FrattiniSubgroup: ",
- GroupString(G.frattiniSubgroup,"F"), "\n");
- return G.frattiniSubgroup;
-
- end;
-
- GroupOps.FrattiniSubgroup := function ( G )
- Error("sorry, can not compute the Frattini subgroup for <G>");
- end;
-
-
- #############################################################################
- ##
- #F NormalClosure( <G>, <U> ) . . . . normal closure of a subgroup in a group
- ##
- NormalClosure := function ( G, U )
- local N; # normal closure of <U> in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
-
- # <G> and <U> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I NormalClosure: of ", GroupString(U,"U"), " in ",
- GroupString(G,"G"), "\n");
-
- # compute the normal closure
- if IsParent( G ) then
- if not IsBound( U.normalClosure ) then
- U.normalClosure := G.operations.NormalClosure( G, U );
- fi;
- N := U.normalClosure;
- else
- N := G.operations.NormalClosure( G, U );
- fi;
-
- # return the normal closure
- InfoGroup1( "#I NormalClosure: returns ", GroupString(N,"N"), "\n" );
- return N;
-
- end;
-
- GroupOps.NormalClosure := function ( G, U )
- local N, # normal closure of <U> in <G>, result
- gensG, # generators of the group <G>
- genG, # one generator of the group <G>
- gensN, # generators of the group <N>
- genN, # one generator of the group <N>
- cnj; # conjugated of a generator of <U>
-
- # get a set of generators of <G>
- if IsFinite( G ) then
- gensG := G.generators;
- else
- gensG := Concatenation(G.generators,List(G.generators,gen->gen^-1));
- fi;
-
- # make a copy of the group to be closed
- N := ShallowCopy( U );
- InfoGroup2("#I |<gens>| = ",Length(N.generators),"\n");
-
- # loop over all generators of N
- gensN := ShallowCopy( N.generators );
- for genN in gensN do
-
- # loop over the generators of G
- for genG in gensG do
-
- # make sure that the conjugated element is in the closure
- cnj := genN ^ genG;
- if not cnj in N then
- InfoGroup2("#I |<gens>| = ",Length(N.generators),"+1\n");
- N := Closure( N, cnj );
- Add( gensN, cnj );
- fi;
-
- od;
-
- od;
-
- # return the normal closure
- return N;
-
- end;
-
-
- #############################################################################
- ##
- #F NormalIntersection( <N>, <U> ) . . . . . intersection with normal subgrp
- ##
- GroupOps.NormalIntersection := GroupOps.Intersection;
-
- NormalIntersection := function( N, U )
- return N.operations.NormalIntersection( N, U );
- end;
-
-
- #############################################################################
- ##
- #F Normalizer( <G>, <U> ) . . . . . . . . . . . . normalizer of a subgroup
- ##
- Normalizer := function ( G, U )
- local N; # normalizer of <U> in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error("<U> must be a group");
- fi;
-
- # <G> and <U> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I Normalizer: of ", GroupString(U,"U"), " in ",
- GroupString(G,"G"), "\n");
-
- # compute the normalizer
- if IsParent( G ) then
- if not IsBound( U.normalizer ) then
- U.normalizer := G.operations.Normalizer( G, U );
- fi;
- N := U.normalizer;
- else
- N := G.operations.Normalizer( G, U );
- fi;
-
- # return the normalizer
- InfoGroup1("#I Normalizer: returns ",GroupString(N,"N"),"\n");
- return N;
-
- end;
-
- GroupOps.Normalizer := function ( G, U )
- return Stabilizer( G, U, G.operations.ConjugateSubgroup );
- end;
-
-
- #############################################################################
- ##
- #F PCore( <G>, <p> ) . . . . . . . . . . . . . . . . . . . p-core of a group
- ##
- ## 'PCore' returns the <p>-core of the group <G>, i.e., the largest normal
- ## <p> subgroup of <G>. This is the core of the <p> Sylow subgroups.
- ##
- PCore := function ( G, p )
-
- # check the arguments
- if not IsGroup( G ) then
- Error("<G> must be a group");
- fi;
- if not IsInt( p ) or p < 0 or not IsPrime( p ) then
- Error("<p> must be a prime");
- fi;
-
- # compute the <p>-core
- if not IsBound( G.pCores ) then
- G.pCores := [];
- fi;
- if not IsBound( G.pCores[p] ) then
- G.pCores[p] := G.operations.PCore( G, p );
- fi;
-
- # return the <p>-core
- return G.pCores[p];
- end;
-
- GroupOps.PCore := function ( G, p )
- return Core( G, SylowSubgroup( G, p ) );
- end;
-
-
- #############################################################################
- ##
- #F Radical( <G> ) . . . . . . . . . . . . . . . . . . . radical of a group
- ##
- ## 'Radical' returns the radical of <G>, i.e., the largest normal solvable
- ## subgroup of <G>.
- ##
- Radical := function ( G )
-
- # check the argument
- if not IsGroup( G ) then
- Error("<G> must be a group");
- fi;
-
- # compute the radical
- if not IsBound( G.radical ) then
- G.radical := G.operations.Radical( G );
- fi;
-
- # return the radical
- return G.radical;
- end;
-
- GroupOps.Radical := function ( G )
- local R, p;
- if IsSolvable( G ) then
- R := G;
- else
- Error("sorry, cannot compute the radical of <G>");
- fi;
- return R;
- end;
-
-
- #############################################################################
- ##
- #F SylowSubgroup( <G>, <p> ) . . . . . . . . . . . Sylowsubgroup of a group
- ##
- SylowSubgroup := function ( G, p )
-
- # check the arguments
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsFinite( G ) then
- Error( "<G> must be a finite group" );
- elif not IsInt( p ) or p < 2 or not IsPrime( p ) then
- Error( "<p> must be a positive prime" );
- fi;
- InfoGroup1( "#I SylowSubgroup: ",GroupString(G,"G"),", <p> = ",p,"\n" );
-
- # compute the Sylow subgroup
- if not IsBound( G.sylowSubgroups ) then
- G.sylowSubgroups := [];
- fi;
- if not IsBound( G.sylowSubgroups[p] ) then
- G.sylowSubgroups[p] := G.operations.SylowSubgroup( G, p );
- fi;
-
- # return the Sylow subgroup
- InfoGroup1("#I SylowSubgroup: ",
- GroupString(G.sylowSubgroups[p],"S"),"\n");
- return G.sylowSubgroups[p];
-
- end;
-
- GroupOps.SylowSubgroup := function ( G, p )
- local S, # <p>-Sylow subgroup of <G>, result
- r; # random element of <G>
-
- # repeat until <S> is the full <p>-Sylow subgroup
- S := TrivialSubgroup( G );
- while Size( G ) / Size( S ) mod p = 0 do
-
- # find an element of order <p> that normalizes <S>
- repeat
- repeat
- r := Random( G );
- until Order( G, r ) mod p = 0;
- r := r ^ (Order( G, r ) / p);
- until not r in S and ForAll( S.generators, g -> g ^ r in S );
-
- # add it to <S>
- S := Closure( S, r );
-
- od;
-
- # return the <p>-Sylow subgroup
- return S;
- end;
-
-
- #############################################################################
- ##
- #F TrivialSubgroup( <G> ) . . . . . . . . . . . trivial subgroup of a group
- ##
- TrivialSubgroup := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
-
- # return the trivial subgroup
- if not IsBound( G.trivialSubgroup ) then
- G.trivialSubgroup := G.operations.TrivialSubgroup( G );
- fi;
- return G.trivialSubgroup;
-
- end;
-
- GroupOps.TrivialSubgroup := function ( G )
- local T;
-
- # check if <G> is a parent group
- if IsBound( G.parent ) then
- T := Subgroup( G.parent, [] );
- else
- T := Subgroup( G, [] );
- fi;
-
- # add the set of elements
- T.elements := [ T.identity ];
- return T;
-
- end;
-
-
- #############################################################################
- ##
-
- #F DerivedSeries( <G> ) . . . . . . . . . . . . . derived series of a group
- ##
- DerivedSeries := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I DerivedSeries: ", GroupString(G,"G"), "\n" );
-
- # compute the derived series
- if not IsBound( G.derivedSeries ) then
- G.derivedSeries := G.operations.DerivedSeries( G );
- fi;
-
- # return the derived series
- InfoGroup1( "#I DerivedSeries returns series of length ",
- Length(G.derivedSeries),"\n");
- return G.derivedSeries;
-
- end;
-
- GroupOps.DerivedSeries := function ( G )
- local S, # derived series of <G>, result
- D; # derived subgroups
-
- # print out a warning for infinite groups
- if not IsFinite( G ) then
- Print("#W DerivedSeries: may not stop for infinite group <G>\n");
- fi;
-
- # compute the series by repeated calling of 'DerivedSubgroup'
- S := [ G ];
- InfoGroup2( "#I DerivedSeries: step ", Length(S), "\n" );
- D := DerivedSubgroup( G );
- while D <> S[ Length(S) ] do
- Add( S, D );
- InfoGroup2( "#I DerivedSeries: step ", Length(S), "\n" );
- D := DerivedSubgroup( D );
- od;
-
- # return the series when it becomes stable
- return S;
-
- end;
-
-
- #############################################################################
- ##
- #F ElementaryAbelianSeries( <G> ) . . elementary abelian series of a group
- ##
- ElementaryAbelianSeries := function( G )
- local L, i;
-
- # if <G> is a list compute a elementary series through a given normal one
- if IsList( G ) then
- if not IsSolvable( G[1] ) then
- Error( "<G> must be solvable" );
- fi;
- for i in [ 1 .. Length(G)-1 ] do
- if not IsNormal(G[1],G[i+1]) or not IsSubgroup(G[i],G[i+1]) then
- Error( "<G> must be normal series" );
- fi;
- od;
- L := G[1].operations.ElementaryAbelianSeries( G );
-
- # otherwise compute a elementary series if it is not known
- else
- if not IsSolvable( G ) then
- Error( "<G> must be solvable" );
- fi;
- if not IsBound( G.elementaryAbelianSeries ) then
- L := G.operations.ElementaryAbelianSeries( G );
- G.elementaryAbelianSeries := L;
- else
- L := G.elementaryAbelianSeries;
- fi;
- fi;
- return L;
-
- end;
-
- GroupOps.ElementaryAbelianSeries := function( G )
- local A, f, L;
-
- # if <G> is a list convert all groups in that list
- if IsList( G ) then
- A := AgGroup( G[1] );
- f := A.bijection^-1;
- L := A.operations.ElementaryAbelianSeries(List(G,x->Image(f,x)));
- f := A.bijection;
-
- # convert <G> into an ag group
- else
- A := AgGroup( G );
- f := A.bijection;
- L := A.operations.ElementaryAbelianSeries( Image( f^-1, G ) );
- fi;
-
- # convert back into <G>
- return List( L, x -> Image( f, x ) );
-
- end;
-
-
- #############################################################################
- ##
- #F CompositionSeries( <G> ) . . . . . . . . . composition series of a group
- ##
- CompositionSeries := function ( G )
-
- if not IsBound( G.compositionSeries ) then
- G.compositionSeries := G.operations.CompositionSeries(G);
- fi;
- return G.compositionSeries;
-
- end;
-
- GroupOps.CompositionSeries := function( G )
- Error( "cannot compute the composition series of <G>" );
- end;
-
-
- #############################################################################
- ##
- #F LowerCentralSeries( <G> ) . . . . . . . . lower central series of a group
- ##
- LowerCentralSeries := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I LowerCentralSeries: ", GroupString(G,"G"), "\n" );
-
- # compute the lower central series
- if not IsBound( G.lowerCentralSeries ) then
- G.lowerCentralSeries := G.operations.LowerCentralSeries( G );
- fi;
-
- # return the lower central series
- InfoGroup1( "#I LowerCentralSeries: returns series of length ",
- Length(G.lowerCentralSeries), "\n" );
- return G.lowerCentralSeries;
-
- end;
-
- GroupOps.LowerCentralSeries := function ( G )
- local S, # lower central series of <G>, result
- C; # commutator subgroups
-
- # print out a warning for infinite groups
- if not IsFinite( G ) then
- Print("#W LowerCentralSeries: may not stop for infinite group <G>\n");
- fi;
-
- # compute the series by repeated calling of 'CommutatorSubgroup'
- S := [ G ];
- InfoGroup2( "#I LowerCentralSeries: step ", Length(S), "\n" );
- C := DerivedSubgroup( G );
- while C <> S[ Length(S) ] do
- Add( S, C );
- InfoGroup2( "#I LowerCentralSeries: step ", Length(S), "\n" );
- C := CommutatorSubgroup( G, C );
- od;
-
- # return the series when it becomes stable
- return S;
-
- end;
-
-
- #############################################################################
- ##
- #F NormalSubgroups( <G> ) . . . . . . . . . . . normal subgroups of a group
- ##
- NormalSubgroups := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I NormalSubgroups: ", GroupString(G,"G"), "\n" );
-
- # compute the normal subgroups
- if not IsBound( G.normalSubgroups ) then
- G.normalSubgroups := G.operations.NormalSubgroups( G );
- fi;
-
- # return the normal subgroups
- InfoGroup1("#I NormalSubgroups: returns ",
- Length(G.normalSubgroups)," subgroups\n");
- return G.normalSubgroups;
-
- end;
-
- GroupOps.NormalSubgroups := function ( G )
- local nrm; # normal subgroups of <G>, result
-
- # compute the normal subgroup lattice above the trivial subgroup
- nrm := G.operations.NormalSubgroupsAbove(G,Subgroup(Parent(G),[]),[]);
-
- # sort the normal subgroups according to their size
- Sort( nrm, function( a, b ) return Size( a ) < Size( b ); end );
-
- # and return it
- return nrm;
-
- end;
-
- GroupOps.NormalSubgroupsAbove := function ( G, N, avoid )
- local R, # normal subgroups above <N>, result
- C, # one conjugacy class of <G>
- g, # representative of a conjugacy class of <G>
- M; # normal closure of <N> and <g>
-
- # initialize the list of normal subgroups
- InfoGroup1("#I normal subgroup of order ",Size(N),"\n");
- R := [ N ];
-
- # make a shallow copy of avoid, because we are going to change it
- avoid := ShallowCopy( avoid );
-
- # for all representative that need not be avoided and do not ly in <N>
- for C in ConjugacyClasses( G ) do
- g := Representative( C );
-
- if not g in avoid and not g in N then
-
- # compute the normal closure of <N> and <g> in <G>
- M := NormalClosure( G, Closure( N, g ) );
- if ForAll( avoid, rep -> not rep in M ) then
- Append( R, G.operations.NormalSubgroupsAbove(G,M,avoid) );
- fi;
-
- # from now on avoid this representative
- Add( avoid, g );
- fi;
- od;
-
- # return the list of normal subgroups
- return R;
-
- end;
-
-
- #############################################################################
- ##
- #F PCentralSeries( <G>, <p> ) . . . . . . . . . . . . . <p>-central series
- ##
- PCentralSeries := function ( G, p )
-
- # <p> must be a prime
- if not IsPrimeInt( p ) then
- Error( "<p> must be a prime" );
- fi;
-
- # check if already know this p-central series
- if not IsBound( G.pCentralSeries ) then
- G.pCentralSeries := [];
- fi;
- if not IsBound( G.pCentralSeries[p] ) then
- G.pCentralSeries[p] := G.operations.PCentralSeries( G, p );
- fi;
- return G.pCentralSeries[p];
-
- end;
-
- GroupOps.PCentralSeries := function( G, p )
- local i, L, C, S, N, P;
-
- # Start with <G>.
- L := [];
- N := G;
- repeat
- Add( L, N );
- S := N;
- C := CommutatorSubgroup( G, S );
- P := Subgroup( Parent(G), List( S.generators, x -> x ^ p ) );
- N := Closure( C, P );
- until N = S;
- return L;
-
- end;
-
-
- #############################################################################
- ##
- #F SubnormalSeries( <G>, <U> ) . subnormal series from a group to a subgroup
- ##
- SubnormalSeries := function ( G, U )
- local S; # subnormal series of <U> in <G>, result
-
- # check thet the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
-
- # <G> and <U> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I SubnormalSeries: of ", GroupString(U,"U"), " in ",
- GroupString(G,"G"), "\n");
-
- # compute the subnormal series
- if IsParent( G ) then
- if not IsBound( U.subnormalSeries ) then
- U.subnormalSeries := G.operations.SubnormalSeries( G, U );
- fi;
- S := U.subnormalSeries;
- else
- #N 9-Dec-91 fceller: we could use a subnormal series of the parent
- S := G.operations.SubnormalSeries( G, U );
- fi;
-
- # return the result
- InfoGroup1( "#I SubnormalSeries: returns series of length ",
- Length( S ),"\n");
- return S;
-
- end;
-
- GroupOps.SubnormalSeries := function ( G, U )
- local S, # subnormal series of <U> in <G>, result
- C; # normal closure of <U> in <G> resp. <C>
-
- # compute the subnormal series by repeated calling of 'NormalClosure'
- S := [ G ];
- InfoGroup2( "#I SubnormalSeries: step ", Length(S), "\n" );
- C := NormalClosure( G, U );
- while C <> S[ Length( S ) ] do
- Add( S, C );
- InfoGroup2( "#I SubnormalSeries: step ", Length(S), "\n" );
- C := NormalClosure( C, U );
- od;
-
- # return the series
- return S;
-
- end;
-
-
- #############################################################################
- ##
- #F UpperCentralSeries( <G> ) . . . . . . . . upper central series of a group
- ##
- UpperCentralSeries := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I UpperCentralSeries: ", GroupString(G,"G"), "\n" );
-
- # compute the upper central series
- if not IsBound( G.upperCentralSeries ) then
- G.upperCentralSeries := G.operations.UpperCentralSeries( G );
- fi;
-
- # return the upper central series
- InfoGroup1( "#I UpperCentralSeries: returns series of length ",
- Length(G.upperCentralSeries), "\n" );
- return G.upperCentralSeries;
-
- end;
-
- GroupOps.UpperCentralSeries := function ( G )
- local S, # upper central series of <G>, result
- C, # centre
- hom; # homomorphisms of <G> to '<G>/<C>'
-
- # print out a warning for infinite groups
- if not IsFinite( G ) then
- Print("#W UpperCentralSeries: may not stop for infinite group <G>\n");
- fi;
-
- # compute the series by repeated calling of 'CommutatorSubgroup'
- S := [ TrivialSubgroup( G ) ];
- InfoGroup2( "#I UpperCentralSeries: step ", Length(S), "\n" );
- C := Centre( G );
- while C <> S[ Length(S) ] do
- Add( S, C );
- InfoGroup2( "#I UpperCentralSeries: step ", Length(S), "\n" );
- hom := NaturalHomomorphism( G, G / C );
- C := PreImage( hom, Centre( hom.range ) );
- od;
-
- # return the series when it becomes stable
- return Reversed( S );
-
- end;
-
-
- #############################################################################
- ##
-
- #F IsAbelian( <G> ) . . . . . . . . . . . . . . test if a group is abelian
- ##
- IsAbelian := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsAbelian: ", GroupString(G,"G"), "\n" );
-
- # test if the group is abelian
- if not IsBound( G.isAbelian ) then
- G.isAbelian := G.operations.IsAbelian( G );
- fi;
-
- # return the result
- InfoGroup1( "#I IsAbelian: returns ", G.isAbelian, "\n" );
- return G.isAbelian;
-
- end;
-
- GroupOps.IsAbelian := function ( G )
- local i, j; # loop variables
-
- # test if every generator commutes with all the others
- for i in [ 2 .. Length(G.generators) ] do
- for j in [ 1 .. i-1 ] do
- if Comm( G.generators[i], G.generators[j] ) <> G.identity then
- return false;
- fi;
- od;
- od;
-
- # all generators commute, return 'true'
- return true;
-
- end;
-
-
- #############################################################################
- ##
- #F IsCentral( <G>, <U> ) . . . . . test if a group is centralizer by another
- ##
- IsCentral := function ( G, U )
- local isCen; # 'true' if <U> is central in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
-
- # <U> and <G> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I IsCentral: is ", GroupString(U,"U"),
- " central in ", GroupString(G,"G"), "\n");
-
- # if <G> is the parent, use the entry '<U>.isCentral'
- if IsParent( G ) then
- if not IsBound( U.isCentral ) then
- U.isCentral := G.operations.IsCentral( G, U );
- fi;
- isCen := U.isCentral;
-
- # otherwise
- else
- if IsBound( U.isCentral ) and U.isCentral then
- isCen := true;
- else
- isCen := G.operations.IsCentral( G, U );
- fi;
- fi;
-
- # return the result
- InfoGroup1( "#I IsCentral: returns ", isCen, "\n" );
- return isCen;
-
- end;
-
- GroupOps.IsCentral := function ( G, U )
- local g, # one generator of <G>
- u; # one generator of <U>
-
- # test if all generators of <U> are fixed by the generators of <G>
- for u in U.generators do
- for g in G.generators do
- if Comm( u, g ) <> U.identity then
- return false;
- fi;
- od;
- od;
-
- # all generators of <U> are fixed, return 'true'
- return true;
-
- end;
-
-
- #############################################################################
- ##
- #F IsConjugate(<G>,<x>,<y>) . test if two objects are conjugated in a group
- ##
- IsConjugate := function ( G, x, y )
- local isConj; # 'true' if <x> and <y> are conjugated, result
-
- # check the arguments
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- if IsGroup( x ) then
- Parent( G, x, y );
- else
- if not x in Parent(G) or not y in Parent(G) then
- Error( "<x> and <y> must lie in the parent group of <G>" );
- fi;
- fi;
- InfoGroup1( "#I IsConjugate: is <x> conjugated to <y> in ",
- GroupString(G,"G"), "\n");
-
- # test if <x> and <y> are conjugated in <G>
- isConj := G.operations.IsConjugate( G, x, y );
-
- # return the result
- InfoGroup1( "#I IsConjugate: returns ",isConj,"\n" );
- return isConj;
-
- end;
-
- GroupOps.IsConjugate := function ( G, x, y )
- return RepresentativeOperation( G, x, y ) <> false;
- end;
-
-
- #############################################################################
- ##
- #F IsCyclic( <G> ) . . . . . . . . . . . . . . . . test if a group is cyclic
- ##
- IsCyclic := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsCyclic: ", GroupString(G,"G"), "\n" );
-
- # test if <G> is cyclic
- if not IsBound( G.isCyclic ) then
- G.isCyclic := G.operations.IsCyclic( G );
- fi;
-
- # return the result
- InfoGroup1( "#I IsCyclic: returns ", G.isCyclic, "\n" );
- return G.isCyclic;
-
- end;
-
- GroupOps.IsCyclic := function ( G )
- local isCyclic; # 'true' if <G> is cyclic, result
-
- # if <G> is not abelian it is certainly not cyclic
- if not IsAbelian( G ) then
- isCyclic := false;
-
- # if <G> has only one generator it is certainly cyclic
- elif Length( G.generators ) <= 1 then
- isCyclic := true;
-
- # if <G> is finite, test if the <p>-th powers of the generators
- # generate a subgroup of index <p> for all prime divisors <p>
- elif IsFinite( G ) then
- isCyclic := ForAll( Set( Factors( Size( G ) ) ),
- p -> Index( G, Subgroup( Parent( G ),
- List(G.generators,g->g^p)) ) = p );
-
- # otherwise test if the abelian invariants are that of $Z$
- else
- isCyclic := AbelianInvariants( G ) = [ 0 ];
- fi;
-
- # return the result
- return isCyclic;
-
- end;
-
-
- #############################################################################
- ##
- #F IsElementaryAbelian( <G> ) . . . . test if a group is elementary abelian
- ##
- IsElementaryAbelian := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsElementaryAbelian: ", GroupString(G,"G"), "\n" );
-
- # test if the group is elementary abelian
- if not IsBound( G.isElementaryAbelian ) then
- G.isElementaryAbelian := G.operations.IsElementaryAbelian( G );
- fi;
-
- # return the result
- InfoGroup1( "#I ",
- "IsElementaryAbelian: returns ",G.isElementaryAbelian,"\n" );
- return G.isElementaryAbelian;
-
- end;
-
- GroupOps.IsElementaryAbelian := function ( G )
- local isEla, # 'true' if <G> is elementary abelian, result
- p; # order of one generator of <G>
-
- # if <G> is not abelian it is certainly not elementary abelian
- if not IsAbelian( G ) then
- isEla := false;
-
- # if <G> is trivial it is certainly elementary abelian
- elif IsTrivial( G ) then
- isEla := true;
-
- # if <G> is infinite it is certainly not elementary abelian
- elif IsBound( G.isFinite ) and not G.isFinite then
- isEla := false;
-
- # otherwise compute the order of the first generator
- else
- p := Order( G, G.generators[1] );
-
- # if the order is not a prime <G> is certainly not elementary abelian
- if not IsPrime( p ) then
- isEla := false;
-
- # otherwise test that all other generators have order <p>
- else
- isEla := ForAll( G.generators, gen -> gen^p = G.identity );
- fi;
-
- fi;
-
- # return the result
- return isEla;
-
- end;
-
-
- #############################################################################
- ##
- #F IsNilpotent( <G> ) . . . . . . . . . . . . test if a group is nilpotent
- ##
- IsNilpotent := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsNilpotent: ", GroupString(G,"G"), "\n" );
-
- # test if <G> is nilpotent
- if not IsBound( G.isNilpotent ) then
- G.isNilpotent := G.operations.IsNilpotent( G );
- fi;
-
- # return the result
- InfoGroup1( "#I IsNilpotent: returns ", G.isNilpotent, "\n" );
- return G.isNilpotent;
-
- end;
-
- GroupOps.IsNilpotent := function ( G )
- local S; # lower central series of <G>
-
- # give a warning if the group is infinite
- if not IsFinite( G ) then
- Print( "#W IsNilpotent: may not stop for infinite group <G>" );
- fi;
-
- # compute the lower central series
- S := LowerCentralSeries( G );
-
- # <G> is nilpotent if the lower central series reaches the trivial group
- return IsTrivial( S[ Length( S ) ] );
-
- end;
-
-
- #############################################################################
- ##
- #F IsNormal( <G>, <U> ) . . . . . test if a group is normalizer by another
- ##
- IsNormal := function ( G, U )
- local isNor; # 'true' if <U> is normal in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error("<U> must be a group");
- fi;
-
- # <U> and <G> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I IsNormal: is ", GroupString(U,"U"),
- " normal in ", GroupString(G,"G"), "\n" );
-
- # if <G> is the parent, use the entry '<U>.isNormal'
- if IsParent( G ) then
- if not IsBound( U.isNormal ) then
- U.isNormal := G.operations.IsNormal( G, U );
- fi;
- isNor := U.isNormal;
-
- # otherwise
- else
- if IsBound( U.isNormal ) and U.isNormal then
- isNor := true;
- else
- isNor := G.operations.IsNormal( G, U );
- fi;
- fi;
-
- # return the result
- InfoGroup1( "#I IsNormal: returns ", isNor, "\n" );
- return isNor;
-
- end;
-
- GroupOps.IsNormal := function ( G, U )
- local gens, # generators of <G>
- gen, # one generator of <G>
- u; # one generator of <U>
-
- # get a generating system of <G>
- if IsFinite( G ) then
- gens := Set( G.generators );
- else
- gens := ShallowCopy( G.generators );
- Append( gens, List( G.generators, gen -> gen ^ -1 ) );
- fi;
-
- # test if all generators of <U> are left in <U>
- for u in U.generators do
- for gen in gens do
- if not u^gen in U then
- return false;
- fi;
- od;
- od;
-
- # all generators of <U> are left in <U>, return 'true'
- return true;
-
- end;
-
-
- #############################################################################
- ##
- #F IsPerfect( <G> ) . . . . . . . . . . . . . . test if a group is perfect
- ##
- IsPerfect := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsPerfect: ", GroupString(G,"G"), "\n" );
-
- # test if <G> is perfect
- if not IsBound( G.isPerfect ) then
- G.isPerfect := G.operations.IsPerfect( G );
- fi;
-
- # return the result
- InfoGroup1( "#I IsPerfect: returns ", G.isPerfect, "\n" );
- return G.isPerfect;
-
- end;
-
- GroupOps.IsPerfect := function ( G )
- local isPerfect; # 'true' if <G> is perfect, result
-
- # if the group is finite test the index of the derived subgroup
- if IsFinite( G ) then
- isPerfect := Index( G, DerivedSubgroup( G ) ) = 1;
-
- # otherwise test the abelian invariants of the commutator factor group
- else
- isPerfect := AbelianInvariants( CommutatorFactorGroup( G ) ) = [];
- fi;
-
- # return the result
- return isPerfect;
-
- end;
-
-
- #############################################################################
- ##
- #F IsSimple( <G> ) . . . . . . . . . . . . . . . . test if a group is simple
- ##
- IsSimple := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsSimple: ", GroupString(G,"G"), "\n" );
-
- # test if <G> is simple
- if not IsBound( G.isSimple ) then
- G.isSimple := G.operations.IsSimple( G );
- fi;
-
- # return the result
- InfoGroup1( "#I IsSimple: returns ", G.isSimple, "\n" );
- return G.isSimple;
-
- end;
-
- GroupOps.IsSimple := function ( G )
- local C, # one conjugacy class of <G>
- g; # representative of <C>
-
- # loop over the conjugacy classes
- for C in ConjugacyClasses( G ) do
- g := Representative( C );
- if g <> G.identity
- and NormalClosure( G, Subgroup( Parent(G), [ g ] ) ) <> G
- then
- return false;
- fi;
- od;
-
- # all classes generate the full group
- return true;
- end;
-
-
- #############################################################################
- ##
- #F IsSolvable( <G> ) . . . . . . . . . . . . . . test if a group is solvable
- ##
- IsSolvable := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- fi;
- InfoGroup1( "#I IsSolvable: ", GroupString(G,"G"), "\n" );
-
- # test if <G> is solvable
- if not IsBound( G.isSolvable ) then
- if IsBound( G.isNilpotent ) and G.isNilpotent then
- G.isSolvable := true;
- elif IsBound( G.isAbelian ) and G.isAbelian then
- G.isSolvable := true;
- else
- G.isSolvable := G.operations.IsSolvable( G );
- fi;
- fi;
-
- # return the result
- InfoGroup1( "#I IsSolvable: returns ", G.isSolvable, "\n" );
- return G.isSolvable;
-
- end;
-
- GroupOps.IsSolvable := function ( G )
- local S; # derived series of <G>
-
- # give a warning for infinite groups, where this may run forever
- if not IsFinite( G ) then
- Print( "#W IsSolvable: may not stop for infinite group <G>" );
- fi;
-
- # compute the derived series of <G>
- S := DerivedSeries( G );
-
- # the group is solvable if the derived series reaches the trivial group
- return IsTrivial( S[ Length( S ) ] );
-
- end;
-
-
- #############################################################################
- ##
- #F IsSubgroup( <G>, <U> ) . . . . test if a group is a subgroup of another
- ##
- IsSubgroup := function ( G, U )
- local isSub;
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
- Parent( G, U );
- InfoGroup1( "#I IsSubgroup: is ", GroupString(U,"U"),
- " a subgroup of ", GroupString(G,"G"), "\n");
-
- # test if <U> is a subgroup of <G>
- if IsParent( G ) then
- isSub := true;
- else
- isSub := G.operations.IsSubgroup( G, U );
- fi;
-
- # return the result
- InfoGroup1( "#I IsSubgroup: returns ", isSub, "\n" );
- return isSub;
-
- end;
-
- GroupOps.IsSubgroup := function ( G, U )
- local isSub; # 'true' if <U> is a subgroup of <G>, result
-
- # if the elements of <G> are known use then
- if IsBound( G.elements ) then
- isSub := IsSubsetSet( G.elements, Set( U.generators ) );
-
- # otherwise test if the generators lie in <G>
- else
- isSub := ForAll( U.generators, gen -> gen in G );
- fi;
-
- # return the result
- return isSub;
-
- end;
-
-
- #############################################################################
- ##
- #F IsSubnormal( <G>, <U> ) . . . . . test if a group is subnormal in another
- ##
- IsSubnormal := function ( G, U )
- local isSub;
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsSubgroup( G, U ) then
- Error( "<U> must be a subgroup of <G>" );
- fi;
-
- # <U> and <G> must have a common parent
- Parent( G, U );
- InfoGroup1( "#I IsSubnormal: is ", GroupString(U,"U"),
- " subnormal in ", GroupString(G,"G"), "\n" );
-
- # if <G> is the parent, use the entry '<U>.isSubnormal'
- if IsParent( G ) then
- if not IsBound( U.isSubnormal ) then
- U.isSubnormal := G.operations.IsSubnormal( G, U );
- fi;
- isSub := U.isSubnormal;
-
- # otherwise
- else
- if IsBound( U.isSubnormal ) and U.isSubnormal then
- isSub := true;
- else
- isSub := G.operations.IsSubnormal( G, U );
- fi;
- fi;
-
- # return the result
- InfoGroup1( "#I IsSubnormal: returns ", isSub, "\n" );
- return isSub;
-
- end;
-
- GroupOps.IsSubnormal := function ( G, U )
- local S; # subnormal series of <U> in <G>
-
- # compute the subnormal series of <U> in <G>
- S := SubnormalSeries( G, U );
-
- # <U> is subnormal if the series reaches <U>
- return S[ Length(S) ] = U;
-
- end;
-
-
- #############################################################################
- ##
- #F IsTrivial( <G> ) . . . . . . . . . . . . . . test if a group is trivial
- ##
- IsTrivial := function ( G )
- return G.operations.IsTrivial( G );
- end;
-
- GroupOps.IsTrivial := function ( G )
- return G.generators = [];
- end;
-
-
- #############################################################################
- ##
-
- #F AbelianInvariants( <G> ) . . . . . . . . . abelian invariants of a group
- ##
- AbelianInvariants := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) or not IsAbelian( G ) then
- Error("<G> must be an abelian group");
- fi;
- InfoGroup1("#I AbelianInvariants: ",GroupString(G,"G"),"\n");
-
- # compute the abelian invariants
- if not IsBound( G.abelianInvariants ) then
- G.abelianInvariants := G.operations.AbelianInvariants( G );
- fi;
-
- # return the abelian invariants
- InfoGroup1( "#I AbelianInvariants: ", G.abelianInvariants, "\n" );
- return G.abelianInvariants;
-
- end;
-
- GroupOps.AbelianInvariants := function ( G )
- local G, H, p, l, r, i, j, gns, inv, ranks, g;
-
- if not IsFinite( G ) then
- Error( "<G> must be a finite group" );
- elif G.generators = [] then
- return [];
- fi;
-
- gns := G.generators;
- inv := [];
- for p in Set( Factors( Size( G ) ) ) do
- ranks := [];
- repeat
- H := TrivialSubgroup( G );
- for g in gns do
- H := Closure( H, g ^ p );
- od;
- r := Size( G ) / Size( H );
- InfoGroup2( "#I AbelianInvariants: |<G>| = ", Size( G ),
- ", |<H>| = ", Size( H ), "\n" );
- G := H;
- gns := G.generators;
- if r <> 1 then Add( ranks, Length( Factors( r ) ) ); fi;
- until r = 1;
- InfoGroup2( "#I AbelianInvariants: <ranks> = ", ranks, "\n" );
- l := List( [ 1 .. ranks[1] ], x -> 1 );
- for i in ranks do
- for j in [ 1 .. i ] do
- l[ j ] := l[ j ] * p;
- od;
- od;
- Append( inv, l );
- od;
-
- Sort( inv );
- return inv;
-
- end;
-
-
- #############################################################################
- ##
- #F Exponent( <G> ) . . . . . . . . . . . . . . . . . . . . . exponent of <G>
- ##
- Exponent := function( G )
-
- # check the argument
- if not IsGroup(G) then
- Error( "<G> must be a group" );
- fi;
-
- # compute the exponent
- if not IsBound(G.exponent) then
- G.exponent := G.operations.Exponent(G);
- fi;
- return G.exponent;
-
- end;
-
- #N 'ConjugacyClasses' will not work with 'FpGroup' so we use 'Elements'
- GroupOps.Exponent := function( G )
- return Lcm( List( Elements(G), x -> Order( G, x ) ) );
- end;
-
-
- #############################################################################
- ##
- #F Index( <G>, <U> ) . . . . . . . . . . . . index of a subgroup in a group
- ##
- Index := function ( G, U )
- local index; # index of <U> in <G>, result
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not IsGroup( U ) then
- Error( "<U> must be a group" );
- fi;
-
- # <G> and <U> must have a common parent
- Parent( G, U );
- InfoGroup1("#I Index: of ", GroupString(U,"U"), " in ",
- GroupString(G,"G"), "\n");
-
- # compute the index
- if IsParent( G ) then
- if not IsBound( U.index ) then
- U.index := G.operations.Index( G, U );
- fi;
- index := U.index;
- else
- index := G.operations.Index( G, U );
- fi;
-
- # return the index
- InfoGroup1("#I Index: returns ",index,"\n");
- return index;
-
- end;
-
- GroupOps.Index := function ( G, U )
- return Size( G ) / Size( U );
- end;
-
-
- #############################################################################
- ##
- #F SmallestGenerators(<G>) . . . . . . smallest generating system of a group
- ##
- SmallestGenerators := function ( G )
-
- # check the argument
- if not IsGroup( G ) then
- Error("<G> must be a group");
- fi;
-
- # compute the smallest generating system
- if not IsBound( G.smallestGenerators ) then
- G.smallestGenerators := G.operations.SmallestGenerators( G );
- fi;
-
- # return the smallest generating system
- return G.smallestGenerators;
- end;
-
- GroupOps.SmallestGenerators := function ( G )
- local gens, # smallest generating system of <G>, result
- gen, # one generator of <gens>
- H; # subgroup generated by <gens> so far
-
- # start with the empty generating system and the trivial subgroup
- gens := [];
- H := TrivialSubgroup( G );
-
- # loop over the elements of <G> in their order
- for gen in Elements( G ) do
-
- # add the element not lying in the subgroup generated by the previous
- if not gen in H then
- Add( gens, gen );
- H := Closure( H, gen );
-
- # it is important to know when to stop
- if Size( H ) = Size( G ) then
- return gens;
- fi;
-
- fi;
-
- od;
-
- # well we should never come here
- Error("panic, <G> not generated by its elements");
- end;
-
-
- #############################################################################
- ##
-
- #F IsConjugacyClass( <C> ) . . test if an object is a conjugacy class record
- ##
- IsConjugacyClass := function ( C )
- return IsRec( C )
- and IsBound( C.isConjugacyClass )
- and C.isConjugacyClass;
- end;
-
-
- #############################################################################
- ##
- #F ConjugacyClass(<G>,<g>) . . . . conjugacy class of an element in a group
- #V ConjugacyClassGroupOps . . . . . operations record for conjugacy classes
- ##
- ConjugacyClass := function ( G, g )
- return G.operations.ConjugacyClass( G, g );
- end;
-
- GroupOps.ConjugacyClass := function ( G, g )
- local C;
-
- # make the domain
- C := rec( );
- C.isDomain := true;
- C.isConjugacyClass := true;
-
- # enter the identifying information
- C.group := G;
- C.representative := g;
-
- # enter the operations record
- C.operations := ConjugacyClassGroupOps;
-
- # return the conjugacy class
- return C;
-
- end;
-
- ConjugacyClassGroupOps := Copy( DomainOps );
-
- ConjugacyClassGroupOps.Elements := function ( C )
- return Set( Orbit( C.group, C.representative ) );
- end;
-
- ConjugacyClassGroupOps.Size := function ( C )
- if not IsBound( C.centralizer ) then
- C.centralizer := Centralizer( C.group, C.representative );
- fi;
- return Index( C.group, C.centralizer );
- end;
-
- ConjugacyClassGroupOps.\= := function ( C, D )
- local isEql;
-
- if IsRec( C ) and IsBound( C.isConjugacyClass )
- and IsRec( D ) and IsBound( D.isConjugacyClass )
- and C.group = D.group
- then
- isEql := C.size = D.size
- and Order( C.group, C.representative )
- = Order( D.group, D.representative )
- and RepresentativeOperation(
- C.group,
- D.representative,
- C.representative ) <> false;
- else
- isEql := DomainOps.\=( C, D );
- fi;
- return isEql;
-
- end;
-
- ConjugacyClassGroupOps.\in := function ( g, C )
- return g in C.group
- and Order( C.group, g ) = Order( C.group, C.representative )
- and RepresentativeOperation( C.group,
- g,
- C.representative ) <> false;
- end;
-
- ConjugacyClassGroupOps.Random := function ( C )
- return C.representative ^ Random( C.group );
- end;
-
- ConjugacyClassGroupOps.\* := function ( C, D )
- if IsConjugacyClass( C ) then
- return Elements( C ) * D;
- elif IsConjugacyClass( D ) then
- return C * Elements( D );
- else
- Error( "panic, neither <C> nor <D> is a conjugacy class" );
- fi;
- end;
-
- ConjugacyClassGroupOps.Print := function ( C )
- Print( "ConjugacyClass( ", C.group, ", ", C.representative, " )" );
- end;
-
-
- #############################################################################
- ##
- #F ConjugacyClasses( <G> ) . . . . . . . . . . conjugacy classes of a group
- ##
- ConjugacyClasses := function ( G )
-
- # check that the argument is a group
- if not IsGroup( G ) then
- Error("<G> must be a group");
- fi;
- InfoGroup1("#I ConjugacyClasses: ",GroupString(G,"G"),"\n");
-
- # compute the conjugacy classes
- if not IsBound( G.conjugacyClasses ) then
- G.conjugacyClasses := G.operations.ConjugacyClasses( G );
- fi;
-
- # return the classes
- InfoGroup1( "#I ConjugacyClasses: returns ",
- Length(G.conjugacyClasses), " classes\n" );
- return G.conjugacyClasses;
-
- end;
-
- GroupOps.ConjugacyClasses := function ( G )
- local classes, # conjugacy classes of <G>, result
- class, # one class of <G>
- elms; # elements of <G>
-
- # initialize the conjugacy class list
- classes := [ ConjugacyClass( G, G.identity ) ];
- InfoGroup1( "#I 1. class of order 1 and length 1 ",
- "(1 of ",Size(G)," elements)\n" );
-
- # if the group is small, or if its elements are known do it the hard way
- if Size( G ) <= 1000 or IsBound( G.elements ) then
-
- # get the elements
- elms := Difference( Elements( G ), [ G.identity ] );
-
- # while we have not found all conjugacy classes
- while elms <> [] do
-
- # add the class of the first element
- class := ConjugacyClass( G, elms[1] );
- Add( classes, class );
- InfoGroup1( "#I ", Length(classes),
- ". class of order ", Order(G,elms[1]),
- " and length ", Size(class),
- " (", Sum(List(classes,Size)), " elements)\n" );
-
- # remove the elements of this class
- SubtractSet( elms, Elements( class ) );
-
- od;
-
- # otherwise use probabilistic algorithm
- else
-
- # while we have not found all conjugacy classes
- while Sum( List( classes, Size ) ) <> Size( G ) do
-
- # try random elements
- G.operations.ConjugacyClassesTry( G, classes, Random(G), 0, 1 );
-
- od;
-
- fi;
-
- # return the conjugacy classes
- return classes;
-
- end;
-
- GroupOps.ConjugacyClassesTry := function ( G, classes, elm, length, fixes )
- local C, # new class
- D, # another new class
- new, # new classes
- i; # loop variable
-
- # if the element is not in one of the known classes add a new class
- if ForAll( classes, D -> length mod Size(D) <> 0 or not elm in D ) then
- C := ConjugacyClass( G, elm );
- Add( classes, C );
- new := [ C ];
- if length = 0 then
- InfoGroup1("#I ",Length(classes),
- ". class of order ",Order(G,elm),
- " and length ",Size(C),
- " (",Sum(List(classes,Size))," elements)\n");
- else
- InfoGroup1("#I ",Length(classes),
- ". class of power order ",Order(G,elm),
- " and length ",Size(C),
- " (",Sum(List(classes,Size))," elements)\n");
- fi;
-
- # try powers that keep the order, compare only with new classes
- for i in [2..Order(G,elm)-1] do
- if GcdInt( i, Order(G,elm) * fixes ) = 1 then
- if not elm^i in C then
- if ForAll( new, D -> not elm^i in D ) then
- D := ConjugacyClass( G, elm^i );
- Add( classes, D );
- Add( new, D );
- InfoGroup1("#I ",Length(classes),
- ". class of same order",
- " and same length ",
- " (",Sum(List(classes,Size)),
- " elements)\n");
- fi;
- elif IsPrime(i) then
- fixes := fixes * i;
- fi;
- fi;
- od;
-
- # try also the powers of this element that reduce the order
- for i in Set( FactorsInt( Order( G, elm ) ) ) do
- G.operations.ConjugacyClassesTry(G,classes,elm^i,Size(C),fixes);
- od;
-
- fi;
-
- end;
-
-
- #############################################################################
- ##
- #F ConjugateSubgroup( <G>, <obj> ) . . . . . . . . . . conjugate of a group
- ##
- ConjugateSubgroup := function ( G, g )
-
- # check the arguments
- if not IsGroup( G ) then
- Error( "<G> must be a group" );
- elif not g in Parent( G ) then
- Error( "<g> must be an element of the parent group of <G>" );
- fi;
-
- # dispatch
- return G.operations.ConjugateSubgroup( G, g );
-
- end;
-
- GroupOps.ConjugateSubgroup := function ( G, g )
- local H, # conjugated subgroup of <G>, result
- name; # component name in the group record
-
- # special case if <g> is in <G>
- if IsBound( G.elements ) and g in G.elements or g in G.generators then
- return G;
- fi;
-
- # create the domain
- H := Subgroup( Parent( G ), OnTuples( G.generators, g ) );
-
- # copy usefull information
- for name in Intersection( RecFields( G ), MaintainedGroupInfo ) do
- H.(name) := G.(name);
- od;
-
- # copy the list of elements if present
- if IsBound( G.elements ) then
- H.elements := OnSets( G.elements, g );
- fi;
-
- # return the conjugated subgroup
- return H;
-
- end;
-
- GroupOps.\^ := ConjugateSubgroup;
-
-
- #############################################################################
- ##
- #F ConjugateSubgroups( <G>, <U> ) . . . conjugated subgroups of a subgroup
- ##
- ConjugateSubgroups := function ( G, U )
-
- # check that the arguments are groups with a common parent
- if not IsGroup( G ) then
- Error("<G> must be a group");
- elif not IsGroup( U ) then
- Error("<U> must be a group");
- fi;
- Parent( G, U );
-
- # dispatch
- return G.operations.ConjugateSubgroups( G, U );
-
- end;
-
- GroupOps.ConjugateSubgroups := function ( G, U )
- return Orbit( G, U );
- end;
-
-
- #############################################################################
- ##
- #F AbstractElementsGroup( <group> ) . . . . elements in abstract generators
- ##
- AbstractElementsGroup := function ( G )
- local elms, e, reps, aReps, aElms, i, k, j;
-
- InfoGroup1( "#I AbstractElementsGroup: ", GroupString( G, "G" ), "\n" );
-
- # start with the identity subgroup
- G.elements := [ G.identity ];
- G.abstractElements := [ IdWord ];
-
- # run over the subgroups <1> <= <G.1> <= <G.1,G.2> <= <G.1,G.2,G.3> ..
- for i in [ 1 .. Length( G.generators ) ] do
-
- # start with the trivial transversal of the previous subgroup
- reps := [ G.identity ];
- aReps := [ IdWord ];
- elms := ShallowCopy( G.elements );
- aElms := ShallowCopy( G.abstractElements );
-
- # perform an orbit algorithm for the representatives
- j := 1;
- while j <= Length( reps ) do
- for k in [ 1 .. i ] do
-
- # if new, add e to representatives and the coset to elements
- e := reps[ j ] * G.generators[ k ];
- if not e in elms then
- Add( reps, e );
- Append( elms, G.elements * e );
- e := aReps[ j ] * G.abstractGenerators[ k ];
- Add( aReps, e );
- Append( aElms, G.abstractElements * e );
- fi;
-
- od;
- j := j + 1;
- od;
-
- # on to the next subgroup
- G.elements := elms;
- G.abstractElements := aElms;
- InfoGroup2( "#I AbstractElementsGroup: |<G.elements>| = ",
- Length( G.elements ), ", ", i, ".th generator\r" );
- od;
-
- # We must sort by hand
- InfoGroup2( "#I AbstractElementsGroup: sorting",
- " \r" );
- e := [ 1 .. Length( G.elements ) ];
- k := function ( a, b ) return G.elements[ a ] < G.elements[ b ]; end;
- Sort( e, k );
- G.elements := Sublist( G.elements, e );
- G.abstractElements := Sublist( G.abstractElements, e );
-
- InfoGroup1( "#I AbstractElementsGroup: |<G>.elements| = ",
- Length( G.elements ), "\n" );
-
- # return the list of elements and abstract elements
- return G;
-
- end;
-
-
- #############################################################################
- ##
- #F Factorization( <G>, <g> ) . . . factorize a group element into generators
- ##
- Factorization := function ( G, g )
- local F;
-
- # compute the factorization
- F := G.operations.Factorization( G, g );
-
- # return the factorization
- return F;
-
- end;
-
- GroupOps.Factorization := function ( G, g )
- local p;
-
- # abort if group is infinite
- if not IsFinite( G ) then
- Error( "sorry, cannot factor <g> in the infinite group <G>" );
- fi;
-
- # we need a list of abstract elements
- if not IsBound( G.abstractElements ) then
- if not IsBound( G.abstractGenerators ) then
- G.abstractGenerators := WordList( Length(G.generators), "x" );
- fi;
- AbstractElementsGroup( G );
- fi;
-
- # is <g> an element of <G> ?
- p := Position( G.elements, g );
- if p = false then
- Error("<g> must be an element of <G>");
- fi;
- return G.abstractElements[ p ];
-
- end;
-
-
- #############################################################################
- ##
- #F AgGroup( <G> ) . . . . . . . . . . . . . . . view a group as an ag group
- ##
- AgGroup := function ( G )
- local H;
-
- if IsBound(G.isPQp) and G.isPQp then
- H := G.operations.AgGroup(G);
- elif not IsGroup(G) then
- Error("<G> must be a group");
- elif not IsFinite(G) then
- Error("<G> must be finite group");
- elif not IsSolvable(G) then
- Error("<G> must be finite solvable group");
- elif IsBound(G.operations.AgGroup) then
- H := G.operations.AgGroup(G);
- else
- Error("sorry, cannot convert <G> into an ag group");
- fi;
- return H;
-
- end;
-
- GroupOps.AgGroup := function ( G )
-
- local D, # derived series of <G>
- B, # ag-system
- BP, # relative order of <B>[i]
- L, # generators of factor groups
- # abstract generators in second part
- M, # <D>[ i+1 ]
- N, # subgroup of <D>[ i ] / M
- Q, # <p>-agemo of <N>
- # composition list in second part
- R, # relators
- p, # one primefactor of |<D>[i] / <D>[i+1]|
- P,
- S,
- i, j;
-
- # Compute the derived series, step down the agemos.
- S := Parent( G );
- D := DerivedSeries( G );
- B := [];
- BP := [];
- for i in [ 1 .. Length( D ) - 1 ] do
- InfoGroup2( "#I AgGroup: derived step ", i, "\n" );
- L := D[ i ].generators;
- M := D[ i + 1 ];
- for p in Set( Factors( Index( D[ i ], M ) ) ) do
- InfoGroup2( "#I AgGroup: prime ", p, "\n" );
- N := Closure( M, Subgroup( S, L ) );
- L := Set( List( L, x -> x ^ p ) );
- Q := Closure( M, Subgroup( S, L ) );
- while N <> Q do
- P := Q;
- j := 1;
- while N <> P do
- while N.generators[ j ] in P do
- j := j + 1;
- od;
- Add( B, N.generators[ j ] );
- Add( BP, p );
- P := Closure( P, N.generators[ j ] );
- od;
- L := Set( List( L, x -> x ^ p ) );
- N := Q;
- Q := Closure( M, Subgroup( S, L ) );
- od;
- od;
- od;
-
- # Now compute a presentation for the ag system <B>
- L := WordList( Length( B ), "g" );
- Q := [];
- j := Length( B );
- for i in [ 2 .. j + 1 ] do
- Q[ i ] := Subgroup( S, Sublist( B, [ i .. j ] ) );
- Q[ i ].abstractGenerators := Sublist( L, [ i .. j ] );
- od;
- R := [];
- for i in [ 1 .. Length( B ) ] do
- Add( R, L[i]^BP[i] / Factorization( Q[ i+1 ], B[i]^BP[i] ) );
- od;
- for i in [ 1 .. Length( B ) - 1 ] do
- for j in [ i + 1 .. Length( B ) ] do
- Add( R, Comm(L[j],L[i])/Factorization(Q[i+1],Comm(B[j],B[i])) );
- od;
- od;
- Q := AgGroupFpGroup( rec( generators := L, relators := R ) );
- Q.bijection := GroupHomomorphismByImages( Q, G, Q.generators, B );
- Q.bijection.isMapping := true;
- Q.bijection.isHomomorphism := true;
- Q.bijection.isIsomorphism := true;
- Q.bijection.isGroupHomomorphism := true;
- Q.bijection.isInjective := true;
- Q.bijection.isSurjective := true;
- Q.bijection.isBijection := true;
- return Q;
-
- end;
-
-
- #############################################################################
- ##
- #F PermGroup( <G> ) . . . . . . . . . . . . . view a group as a perm group
- ##
- PermGroup := function ( G )
-
- # check that the argument is a finite group
- if not IsGroup( G ) or not IsFinite( G ) then
- Error("<G> must be a finite group");
- fi;
-
- # find a permutation group
- if not IsBound( G.permGroup ) then
- G.permGroup := G.operations.PermGroup( G );
- fi;
-
- # return the permutation group
- InfoGroup1("#I PermGroup: returns ",GroupString(G.permGroup,"P"),"\n");
- return G.permGroup;
-
- end;
-
- GroupOps.PermGroup := function ( G )
- local P; # permutation group isomorphic to <G>, result
-
- # make the permutation group
- P := Operation( G, Elements( G ), OnRight );
-
- # make the bijection from <P> to <G>
- P.bijection := InverseMapping( OperationHomomorphism( G, P ) );
- P.bijection.isMapping := true;
- P.bijection.isInjective := true;
- P.bijection.isSurjective := true;
- P.bijection.isBijection := true;
- P.bijection.isHomomorphism := true;
- P.bijection.isMonomorphism := true;
- P.bijection.isEpimorphism := true;
- P.bijection.isIsomorphism := true;
- P.bijection.image := G;
- P.bijection.preimage := P;
- P.bijection.kernel := TrivialSubgroup( P );
-
- # return the permutation group
- return P;
-
- end;
-
-
- #############################################################################
- ##
-
- #R Read . . . . . . . . . . . . . read other function from the other files
- ##
- ReadLib( "grphomom" );
- ReadLib( "operatio" );
- ReadLib( "grpcoset" );
- ReadLib( "grpprods" );
- ReadLib( "grpctbl" );
- ReadLib( "lattgrp" );
-
-
- #############################################################################
- ##
- #E Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
- ##
- ## Local Variables:
- ## mode: outline
- ## outline-regexp: "#F\\|#V\\|#E\\|#R"
- ## fill-column: 73
- ## fill-prefix: "## "
- ## eval: (hide-body)
- ## End:
- ##
-
-
-
-