home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-05-05 | 63.9 KB | 1,965 lines |
- #############################################################################
- ##
- #A fpgrp.g GAP library Martin Schoenert
- ##
- #A @(#)$Id: fpgrp.g,v 3.12 1993/02/09 14:25:55 martin Rel $
- ##
- #Y Copyright 1990-1992, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
- ##
- ## This file contains the functions dealing with finitely presented groups.
- ##
- #H $Log: fpgrp.g,v $
- #H Revision 3.12 1993/02/09 14:25:55 martin
- #H made undefined globals local
- #H
- #H Revision 3.11 1992/12/16 19:47:27 martin
- #H replaced quoted record names with escaped ones
- #H
- #H Revision 3.10 1992/12/03 12:57:43 fceller
- #H renamed 'IsEquivalent' to 'IsIdentical'
- #H
- #H Revision 3.9 1992/10/07 08:43:30 martin
- #H fixed another minor problem in 'LowIndexSubgroupsFpGroup'
- #H
- #H Revision 3.8 1992/10/02 17:38:39 martin
- #H added 'FpGroupOps.GroupHomomorphismByImages'
- #H
- #H Revision 3.7 1992/10/02 17:20:33 martin
- #H fixed 'LowIndexSubgroupsFpGroup'
- #H
- #H Revision 3.6 1992/10/02 17:14:30 martin
- #H added some information printing to 'CosetTableFpGroup'
- #H
- #H Revision 3.5 1992/08/14 15:41:31 fceller
- #H 'FpGroupOps.AbelianInvariants' now calls 'DiagonalizeMat'
- #H instead of 'ElementaryDivisorsMat'
- #H
- #H Revision 3.4 1992/08/14 09:24:26 fceller
- #H replaced handle comparision by 'IsEquivalent'
- #H
- #H Revision 3.3 1992/07/15 13:36:19 martin
- #H added packages "fpsgpres" and "fptietze"
- #H
- #H Revision 3.2 1992/07/15 12:42:39 martin
- #H added 'RelsSortedByStartGens' and modified 'CosetTableFpGroup'
- #H
- #H Revision 3.1 1992/04/07 19:41:42 martin
- #H initial revision under RCS
- #H
- ##
-
-
- #############################################################################
- ##
- #F InfoFpGroup?(...) . . . . . information function for the fp group package
- ##
- if not IsBound(InfoFpGroup1) then InfoFpGroup1 := Ignore; fi;
- if not IsBound(InfoFpGroup2) then InfoFpGroup2 := Ignore; fi;
-
-
- #############################################################################
- ##
- #F FreeGroup( <rank> [, <name> ] ) . . . . . . . . free group of given rank
- ##
- FreeGroup := function ( arg )
- local F, # free group of rank <rank>, result
- gens, # generators of <F>
- rank, # rank of the free group, first argument
- name, # name of the group, optional second argument
- i; # loop variable
-
- # get and check the argument list
- rank := arg[1];
- if Length( arg ) = 1 then
- name := "f";
- elif Length( arg ) = 2 then
- name := arg[2];
- else
- Error("usage: FreeGroup( <rank> [, <name>] )");
- fi;
-
- # make the generators
- gens := [];
- for i in [ 1 .. arg[1] ] do
- gens[i] := AbstractGenerator(
- ConcatenationString( name, ".", String(i) )
- );
- od;
-
- # make the group
- F := Group( gens, IdWord );
-
- # return the group
- return F;
- end;
-
-
- #############################################################################
- ##
- #V Words . . . . . . . . . . . . . . . . . . . . . . . . domain of all words
- #V WordsOps . . . . . . . . . operations record for the domain of all words
- ##
- Words := rec();
- Words.isDomain := true;
-
- Words.name := "Words";
-
- Words.isFinite := false;
- Words.size := "infinity";
-
- Words.operations := Copy( GroupElementsOps );
- WordsOps := Words.operations;
-
-
- #############################################################################
- ##
- #F WordsOps.\in( <g>, Words ) . membership test for the domain of all words
- ##
- WordsOps.\in := function ( g, Words )
- return IsWord( g );
- end;
-
-
- #############################################################################
- ##
- #F IsFpGroup(<D>) . . . . . . . . . . . . . is an object a fin. pres. group
- ##
- IsFpGroup := function ( obj )
- return IsRec( obj )
- and IsBound( obj.isFpGroup ) and obj.isFpGroup;
- end;
-
-
- #############################################################################
- ##
- #F WordsOps.Group . . . . . . . . . . . . . . . . create a fin. pres. group
- ##
- WordsOps.Group := function ( Words, gens, id )
- local G; # finitely presented group, result
-
- # check that all generators have length 1
- if not ForAll( gens, g -> LengthWord( g ) = 1 ) then
- Error("the generators must have length 1 (maybe use 'Subgroup')");
- fi;
-
- # let the default function do the main work
- G := GroupElementsOps.Group( Words, gens, id );
-
- # add the tag
- G.isFpGroup := true;
-
- # add the operations record
- G.operations := FpGroupOps;
-
- # return the group
- return G;
- end;
-
-
- #############################################################################
- ##
- #V FpGroupOps . . . . . . . . . . . operations record for fin. pres. groups
- ##
- FpGroupOps := Copy( GroupOps );
-
-
- #############################################################################
- ##
- #F FpGroupOps.Subgroup(<G>,<gens>) . make a subgroup of a fin. pres. group
- ##
- FpGroupOps.Subgroup := function ( G, gens )
- local S; # subgroup, result
-
- # let the default function do the main work
- S := GroupOps.Subgroup( G, gens );
-
- # add the finitely presented groups tag
- S.isFpGroup := true;
-
- # add the finitely presented groups operations record
- S.operations := FpGroupOps;
-
- # return the subgroup
- return S;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.TrivialSubgroup(<G>) . trivial subgroup of a fin. pres. group
- ##
- FpGroupOps.TrivialSubgroup := function ( G )
- local T; # trivial subgroup of <G>, result
-
- # let the default function do the main work
- T := GroupOps.TrivialSubgroup( G );
-
- # remove the elements list
- Unbind( T.elements );
-
- # return the trivial subgroup
- return T;
- end;
-
-
- #############################################################################
- ##
- #F CyclicPermutationsWords( <words> ) . . . . . set of cyclic permutations
- #F of a list of words
- ##
- ## 'CyclicPermutationsWords' returns the set of all the cyclic
- ## permutations of the words in the list <words> and their inverses.
- ##
- ## The Todd Coxeter with the Felsch strategy needs the extended set of
- ## relators, it is also sometimes useful when using the HLT strategy.
- ##
- CyclicPermutationsWords := function ( words )
- local cycperms, word, w;
- cycperms := [];
- for word in words do
- while LengthWord( word^Subword(word,1,1) ) < LengthWord( word ) do
- word := word ^ Subword(word,1,1);
- od;
- if not word in cycperms then
- w := word;
- repeat
- AddSet( cycperms, w );
- AddSet( cycperms, w^-1 );
- w := w ^ Subword( w, 1, 1 );
- until w = word;
- fi;
- od;
- return cycperms;
- end;
-
-
- #############################################################################
- ##
- #F RelatorRepresentatives( <rels> ) . . . . . . set of representatives of a
- #F list of relators
- ##
- ## 'RelatorRepresentatives' returns a set of cyclically reduced represent-
- ## atives, with respect to conjugation or inversion, of the relators in the
- ## list <rels>.
- ##
- ## The Todd Coxeter with the Felsch strategy needs the extended set of
- ## relators, it is also sometimes useful when using the HLT strategy.
- ## Moreover, it is used by the Reduced Reidemeister-Schreier.
- ##
- RelatorRepresentatives := function ( rels )
-
- local contained, invreps, rel, relreps, word;
-
- relreps := [];
- invreps := [];
- for rel in rels do
- while LengthWord( rel^Subword( rel, 1, 1 ) ) < LengthWord( rel ) do
- rel := rel ^ Subword( rel, 1, 1 );
- od;
- word := rel;
- contained := word = [] or word in relreps or word in invreps;
- while not contained do
- word := word ^ Subword( word, 1, 1 );
- if word = rel then
- Add( relreps, word );
- Add( invreps, word^-1 );
- contained := true;
- else
- contained := word = [] or word in relreps or word in invreps;
- fi;
- od;
- od;
-
- return relreps;
- end;
-
-
- #############################################################################
- ##
- #F RelsSortedByStartGen( <parent group>, <coset table> [,<sort>] ) . . . . .
- #F relators sorted by start generator
- ##
- ## 'RelsSortedByStartGen' is a subroutine of the Felsch Todd-Coxeter and
- ## the Reduced Reidemeister-Schreier routines. It returns a list which for
- ## each generator or inverse generator contains a list of all cyclically
- ## reduced relators, starting with that element, which can be obtained by
- ## conjugating or inverting given relators. The relators are represented as
- ## lists of the coset table columns corresponding to the generators and, in
- ## addition, as lists of the respective column numbers.
- ##
- ## If a third argument is specified and equal to true, then the resulting
- ## list will be sorted.
- ##
- RelsSortedByStartGen := function ( arg )
-
- local base, base2, cols, extleng, G, gen, i, invcols, invnums, j, k,
- length, less, numcols, numgens, nums, p, p1, p2, rel, relsGen,
- sort, sortlist, table, word;
-
-
- less := function ( triple1, triple2 )
-
- # 'less' defines an ordering on the triples [ nums, cols, startpos ]
- # in list relsGen.
- local diff, i, k, nums1, nums2;
-
- if triple1[1][1] <> triple2[1][1] then
- return( triple1[1][1] < triple2[1][1] );
- fi;
-
- nums1 := triple1[1]; nums2 := triple2[1];
- i := triple1[3];
- diff := triple2[3] - i;
- k := i + nums1[1] + 2;
- while i < k do
- if nums1[i] <> nums2[i+diff] then
- return( nums1[i] < nums2[i+diff] );
- fi;
- i := i + 2;
- od;
-
- return( false );
- end;
-
-
- # get the arguments.
- G := arg[1];
- table := arg[2];
- sort := false;
- if Length( arg ) > 2 then sort := arg[3]; fi;
-
- # check table length and number of generators to be consistent.
- numgens := Length( G.generators );
- numcols := Length( table );
- if numcols <> 2 * numgens then
- Error( "table length is inconsistent with number of generators" );
- fi;
-
- # initialize the list to be constructed.
- relsGen := 0 * [1 .. numcols];
- for i in [ 1 .. numcols ] do
- if Mod( i, 2 ) = 1 or not IsIdentical( table[i], table[i-1] ) then
- relsGen[i] := [ ];
- else
- relsGen[i] := relsGen[i-1];
- fi;
- od;
-
- # now loop over all parent group relators.
- for rel in RelatorRepresentatives( G.relators ) do
-
- # get the length and the basic length of relator rel.
- length := LengthWord( rel );
- base := 1;
- word := rel ^ Subword( rel, 1, 1 );
- while word <> rel do
- base := base + 1;
- word := word ^ Subword( word, 1, 1 );
- od;
-
- if length = 2 and base = 1 then
-
- # check the table columns corresponding to an involutory
- # generator and its inverse to be identical.
- gen := Subword( rel, 1, 1 );
- p := Position( G.generators, gen );
- if p = false then p := Position( G.generators, gen^-1 ); fi;
- if not IsIdentical( table[2*p-1], table[2*p] ) then
- Error( "table inconsistent with square relators" );
- fi;
-
- else
-
- # initialize the columns and numbers lists corresponding to the
- # current relator.
- base2 := 2 * base;
- extleng := 2 * ( base + length ) - 1;
- nums := 0 * [1 .. extleng]; invnums := 0 * [1 .. extleng];
- cols := 0 * [1 .. extleng]; invcols := 0 * [1 .. extleng];
-
- # compute the lists.
- i := 0; j := 1; k := base2 + 3;
- while i < base do
- i := i + 1; j := j + 2; k := k - 2;
- gen := Subword( rel, i, i );
- p := Position( G.generators, gen );
- if p = false then
- p := Position( G.generators, gen^-1 );
- p1 := 2 * p;
- p2 := 2 * p - 1;
- else
- p1 := 2 * p - 1;
- p2 := 2 * p;
- fi;
- nums[j] := p1; invnums[k-1] := p1;
- nums[j-1] := p2; invnums[k] := p2;
- cols[j] := table[p1]; invcols[k-1] := table[p1];
- cols[j-1] := table[p2]; invcols[k] := table[p2];
- Add( relsGen[p1], [ nums, cols, j ] );
- Add( relsGen[p2], [ invnums, invcols, k ] );
- od;
-
- while j < extleng do
- j := j + 1;
- nums[j] := nums[j-base2]; invnums[j] := invnums[j-base2];
- cols[j] := cols[j-base2]; invcols[j] := invcols[j-base2];
- od;
-
- nums[1] := length; invnums[1] := length;
- cols[1] := 2 * length - 3; invcols[1] := cols[1];
- fi;
- od;
-
- if sort then
- # sort the resulting lists to get better results of the Reduced Rei-
- # demeister-Schreier (this is not needed for the Felsch Todd-Coxeter)
- for i in [ 1 .. numcols ] do
- Sort( relsGen[i], less );
- od;
- fi;
-
- return relsGen;
- end;
-
-
- #############################################################################
- ##
- #F CosetTableFpGroup(<G>,<H>) . . . . . . . . . . . do a coset enumeration
- ##
- ## 'CosetTableFpGroup' applies a Felsch strategy Todd-Coxeter coset
- ## enumeration to construct a coset table of H in G.
- ##
- if not IsBound( CosetTableFpGroupDefaultLimit ) then
- CosetTableFpGroupDefaultLimit := 1000;
- fi;
-
- CosetTableFpGroup := function ( G, H )
- local next, prev, # next and previous coset on lists
- firstFree, lastFree, # first and last free coset
- firstDef, lastDef, # first and last defined coset
- firstCoinc, lastCoinc, # first and last coincidence coset
- table, # columns in the table for gens
- relsGen, # relators sorted by start generator
- subgroup, # rows for the subgroup gens
- deductions, # deduction queue
- i, gen, inv, # loop variables for generator
- g, # loop variable for generator col
- rel, # loop variable for relation
- p, p1, p2, # generator position numbers
- app, # arguments list for 'MakeConsequences'
- limit, # limit of the table
- j, # integer variable
- length, length2, # length of relator
- cols,
- gen,
- nums,
- l,
- nrdef, # number of defined cosets
- nrmax, # maximal value of the above
- nrdel, # number of deleted cosets
- nrinf; # number for next information message
-
- # check the arguments
- if not IsParent( G ) or G <> Parent( H ) then
- Error( "<G> must be the parent group of <H>" );
- fi;
-
- # give some information
- InfoFpGroup1( "#I ", "CosetTableFpGroup called:\n" );
- InfoFpGroup2( "#I defined deleted alive maximal\n");
- nrdef := 1;
- nrmax := 1;
- nrdel := 0;
- nrinf := 1000;
-
- # initial size of the table
- limit := CosetTableFpGroupDefaultLimit;
-
- # define one coset (1)
- firstDef := 1; lastDef := 1;
- firstFree := 2; lastFree := limit;
-
- # make the lists that link together all the cosets
- next := [2..limit+1]; next[1] := 0; next[limit] := 0;
- prev := [0..limit-1]; prev[2] := 0;
-
- # make the columns for the generators
- table := [];
- for gen in G.generators do
- g := 0 * [1..limit];
- Add( table, g );
- if not ( gen^2 in G.relators or gen^-2 in G.relators ) then
- g := 0 * [1..limit];
- fi;
- Add( table, g );
- od;
-
- # make the rows for the relators and distribute over relsGen
- relsGen := RelsSortedByStartGen( G, table );
-
- # make the rows for the subgroup generators
- subgroup := [];
- for rel in H.generators do
- length := LengthWord( rel );
- length2 := 2 * length;
- nums := 0 * [1 .. length2];
- cols := 0 * [1 .. length2];
-
- # compute the lists.
- i := 0; j := 0;
- while i < length do
- i := i + 1; j := j + 2;
- gen := Subword( rel, i, i );
- p := Position( G.generators, gen );
- if p = false then
- p := Position( G.generators, gen^-1 );
- p1 := 2 * p;
- p2 := 2 * p - 1;
- else
- p1 := 2 * p - 1;
- p2 := 2 * p;
- fi;
- nums[j] := p1; cols[j] := table[p1];
- nums[j-1] := p2; cols[j-1] := table[p2];
- od;
- Add( subgroup, [ nums, cols ] );
- od;
-
- # add an empty deduction list
- deductions := [];
-
- # make the structure that is passed to 'MakeConsequences'
- app := [ table, next, prev, relsGen, subgroup ];
-
- # run over all the cosets
- while firstDef <> 0 do
-
- # run through all the rows and look for undefined entries
- for i in [ 1 .. Length( table ) ] do
- gen := table[i];
-
- if gen[firstDef] = 0 then
-
- inv := table[i + 2*(i mod 2) - 1];
-
- # if necessary expand the table
- if firstFree = 0 then
- next[2*limit] := 0;
- prev[2*limit] := 2*limit-1;
- for g in table do g[2*limit] := 0; od;
- for l in [limit+2..2*limit-1] do
- next[l] := l+1;
- prev[l] := l-1;
- for g in table do g[l] := 0; od;
- od;
- next[limit+1] := limit+2;
- prev[limit+1] := 0;
- for g in table do g[limit+1] := 0; od;
- firstFree := limit+1;
- limit := 2*limit;
- lastFree := limit;
- fi;
-
- # update the debugging information
- nrdef := nrdef + 1;
- if nrmax <= firstFree then
- nrmax := firstFree;
- fi;
-
- # define a new coset
- gen[firstDef] := firstFree;
- inv[firstFree] := firstDef;
- next[lastDef] := firstFree;
- prev[firstFree] := lastDef;
- lastDef := firstFree;
- firstFree := next[firstFree];
- next[lastDef] := 0;
-
- # set up the deduction queue and run over it until it's empty
- app[6] := firstFree;
- app[7] := lastFree;
- app[8] := firstDef;
- app[9] := lastDef;
- app[10] := i;
- app[11] := firstDef;
- nrdel := nrdel + MakeConsequences( app );
- firstFree := app[6];
- lastFree := app[7];
- firstDef := app[8];
- lastDef := app[9];
-
- # give some information
- while nrinf <= nrdef+nrdel do
- InfoFpGroup2( "#I\t", nrdef, "\t", nrinf-nrdef, "\t",
- 2*nrdef-nrinf, "\t", nrmax, "\n" );
- nrinf := nrinf + 1000;
- od;
-
- fi;
- od;
-
- firstDef := next[firstDef];
- od;
-
- InfoFpGroup1( "#I\t", nrdef, "\t", nrdel, "\t",
- nrdef-nrdel, "\t", nrmax, "\n" );
-
- # standardize the table
- StandardizeTable( table );
-
- # return the table
- return table;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.\in( <w>, <G> ) . . . . membership test for fin. pres. groups
- ##
- FpGroupOps.\in := function ( w, H )
- local G, # parent of <H>
- g, # one generator of <G>
- c, # coset in tracing
- i; # loop variable
-
- # handle trivial case first
- if not IsWord( w ) then
- return false;
-
- # handle the parent group by testing the letters of the word
- elif IsParent( H ) then
- for i in [ 1 .. LengthWord( w ) ] do
- g := Subword( w, i, i );
- if not g in H.generators and not g^-1 in H.generators then
- return false;
- fi;
- od;
- return true;
-
- # otherwise trace the word through the coset table
- else
- G := Parent( H );
- if not IsBound( H.cosetTable ) then
- H.cosetTable := CosetTableFpGroup( G, H );
- fi;
- c := 1;
- for i in [ 1 .. LengthWord( w ) ] do
- g := Subword( w, i, i );
- if g in G.generators then
- c := H.cosetTable[ 2*Position(G.generators,g)-1 ][ c ];
- elif g^-1 in G.generators then
- c := H.cosetTable[ 2*Position(G.generators,g^-1) ][ c ];
- else
- return false;
- fi;
- od;
- return c = 1;
-
- fi;
-
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.IsSubset(<G>,<H>) . . . . is one fp group a subset of another
- ##
- FpGroupOps.IsSubset := function ( G, H )
- local isSub;
-
- # avoid calling 'IsFinite' as in 'GroupOps.IsSubset'
- if IsGroup( G ) then
- if 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 ) then
- isSub := IsSubset( G, H.group )
- and H.representative in G;
- else
- isSub := DomainOps.IsSubset( G, H );
- fi;
- elif IsCoset( G ) then
- if IsGroup( H ) then
- isSub := H.identity in G
- 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 FpGroupOps.Size(<G>) . . . . . . . . . . . . size of a fin. pres. group
- ##
- FpGroupOps.Size := function ( G )
-
- # handle free group
- if IsParent( G ) and not IsBound( G.relators ) then
- return "infinity";
-
- # handle parent group by computing the index of the trivial subgroup
- elif IsParent( G ) then
- return Index( G, TrivialSubgroup( G ) );
-
- # handle other groups via 'Index'
- else
- return Size( Parent( G ) ) / Index( Parent( G ), G );
- fi;
-
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Index(<G>,<H>) . . . . . . . . . . . . . . index of subgroups
- ##
- FpGroupOps.Index := function ( G, H )
- if IsParent( G ) then
- if not IsBound( H.cosetTable ) then
- H.cosetTable := CosetTableFpGroup( G, H );
- fi;
- return Length( H.cosetTable[1] );
- else
- return Index( Parent( H ), H ) / Index( Parent( G ), G );
- fi;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Elements(<G>) . . . . . . . . elements of a fin. pres. group
- ##
- FpGroupOps.Elements := function ( G )
- local elms, # elements of <G>, result
- table, # coset table of <1> in <G>
- c, # one coset in of <1> in <G>
- i, k, l; # loop variables
-
- # handle parent groups
- if IsParent( G ) then
- if Size( G ) = "infinity" then
- Error("sorry cannot list the elements of the free group <G>");
- fi;
- table := G.trivialSubgroup.cosetTable;
- elms := [ IdWord ];
- for i in [ 2 .. Length( table[1] ) ] do
- k := 1;
- for l in [ 2 .. Length( G.generators ) ] do
- if table[ 2*l ][ i ] < table[ 2*k ][ i ] then
- k := l;
- fi;
- od;
- Add( elms, elms[ table[ 2*k ][ i ] ] * G.generators[ k ] );
- od;
- return elms;
-
- # otherwise
- else
- elms := Filtered( Elements( Parent( G ) ), elm -> elm in G );
- return elms;
- fi;
-
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Intersection(<G>,<H>) . intersection of two fin. pres. groups
- ##
- FpGroupOps.Intersection := function ( G, H )
- local I, # intersection of <G> and <H>, result
- table, # coset table for <I> in its parent
- nrcos, # number of cosets of <I>
- tableG, # coset table of <G>
- nrcosG, # number of cosets of <G>
- tableH, # coset table of <H>
- nrcosH, # number of cosets of <H>
- nrgens, # number of generators of the parent of <G> and <H>
- ren, # if 'ren[<i>]' is 'nrcosH * <iG> + <iH>' then the
- # coset <i> of <I> corresponds to the intersection
- # of the pair of cosets <iG> of <G> and <iH> of <H>
- ner, # the inverse mapping of 'ren'
- cos, # coset loop variable
- gen, # generator loop variable
- img; # image of <cos> under <gen>
-
- # delegate exceptional case
- if Parent( G ) <> Parent( H ) then
- return DomainOps.Intersection( G, H );
- fi;
-
- # handle trivial cases
- if IsParent( G ) then
- return H;
- elif IsParent( H ) then
- return G;
- fi;
-
- # make sure both subgroups have a coset table
- if not IsBound( G.cosetTable ) then
- G.cosetTable := CosetTableFpGroup( Parent( G ), G );
- fi;
- tableG := G.cosetTable;
- nrcosG := Length( tableG[1] ) + 1;
- if not IsBound( H.cosetTable ) then
- H.cosetTable := CosetTableFpGroup( Parent( H ), H );
- fi;
- tableH := H.cosetTable;
- nrcosH := Length( tableH[1] ) + 1;
-
- # initialize the table for the intersection
- nrgens := Length( Parent( G ).generators );
- table := [];
- for gen in [ 1 .. nrgens ] do
- table[ 2*gen-1 ] := [];
- if Parent( G ).generators[ gen ]^2 in Parent( G ).relators
- or Parent( G ).generators[ gen ]^-2 in Parent( G ).relators
- then
- table[ 2*gen ] := table[ 2*gen-1 ];
- else
- table[ 2*gen ] := [];
- fi;
- od;
-
- # set up the renumbering
- ren := 0 * [ 1 .. nrcosG * nrcosH ];
- ner := 0 * [ 1 .. nrcosG * nrcosH ];
- ren[ 1*nrcosH + 1 ] := 1;
- ner[ 1 ] := 1*nrcosH + 1;
- nrcos := 1;
-
- # the coset table for the intersection is the transitive component of 1
- # in the *tensored* permutation representation
- cos := 1;
- while cos <= nrcos do
-
- # loop over all entries in this row
- for gen in [ 1 .. nrgens ] do
-
- # get the coset pair
- img := nrcosH * tableG[ 2*gen-1 ][ QuoInt( ner[ cos ], nrcosH ) ]
- + tableH[ 2*gen-1 ][ ner[ cos ] mod nrcosH ];
-
- # if this pair is new give it the next available coset number
- if ren[ img ] = 0 then
- nrcos := nrcos + 1;
- ren[ img ] := nrcos;
- ner[ nrcos ] := img;
- fi;
-
- # and enter it into the coset table
- table[ 2*gen-1 ][ cos ] := ren[ img ];
- table[ 2*gen ][ ren[ img ] ] := cos;
-
- od;
-
- cos := cos + 1;
- od;
-
- # now make the subgroup
- I := Subgroup( Parent( G ), GeneratorsCosetTable( Parent( G ), table ) );
- I.cosetTable := table;
-
- # and return it
- return I;
- end;
-
- GeneratorsCosetTable := function ( G, table )
- local gens, # generators for the subgroup
- relsGen, # relators sorted by start generator
- deductions, # deduction queue
- ded, # index of current deduction in above
- nrdeds, # current number of deductions in above
- nrgens, # number of generators of <G>
- cos, # loop variable for coset
- i, gen, inv, # loop variables for generator
- g, # loop variable for generator col
- rel, # loop variable for relation
- p, p1, p2, # generator position numbers
- triple, # loop variable for relators as triples
- app, # arguments list for 'ApplyRel'
- x, y, c;
-
- nrgens := 2 * Length( G.generators ) + 1;
- gens := [];
-
- # make all entries in the table negative
- for cos in [ 1 .. Length( table[1] ) ] do
- for gen in table do
- if 0 < gen[cos] then
- gen[cos] := -gen[cos];
- fi;
- od;
- od;
-
- # make the rows for the relators and distribute over relsGen
- relsGen := RelsSortedByStartGen( G, table );
-
- # make the structure that is passed to 'ApplyRel'
- app := 0 * [ 1 .. 4 ];
-
- # run over all the cosets
- cos := 1;
- while cos <= Length( table[1] ) do
-
- # run through all the rows and look for undefined entries
- for i in [1..Length(G.generators)] do
- gen := table[2*i-1];
-
- if gen[cos] < 0 then
-
- inv := table[2*i];
-
- # make the Schreier generator for this entry
- x := IdWord;
- c := cos;
- while c <> 1 do
- g := nrgens - 1;
- y := nrgens - 1;
- while 0 < g do
- if AbsInt(table[g][c]) <= AbsInt(table[y][c]) then
- y := g;
- fi;
- g := g - 2;
- od;
- x := G.generators[ y/2 ] * x;
- c := AbsInt(table[y][c]);
- od;
- x := x * G.generators[ i ];
- c := AbsInt( gen[ cos ] );
- while c <> 1 do
- g := nrgens - 1;
- y := nrgens - 1;
- while 0 < g do
- if AbsInt(table[g][c]) <= AbsInt(table[y][c]) then
- y := g;
- fi;
- g := g - 2;
- od;
- x := x * G.generators[ y/2 ]^-1;
- c := AbsInt(table[y][c]);
- od;
- if x <> IdWord then
- Add( gens, x );
- fi;
-
- # define a new coset
- gen[cos] := - gen[cos];
- inv[ gen[cos] ] := cos;
-
- # set up the deduction queue and run over it until it's empty
- deductions := [ [i,cos] ];
- nrdeds := 1;
- ded := 1;
- while ded <= nrdeds do
-
- # apply all relators that start with this generator
- for triple in relsGen[deductions[ded][1]] do
- app[1] := triple[3];
- app[2] := deductions[ded][2];
- app[3] := -1;
- app[4] := app[2];
- if ApplyRel( app, triple[2] ) then
- triple[2][app[1]][app[2]] := app[4];
- triple[2][app[3]][app[4]] := app[2];
- nrdeds := nrdeds + 1;
- deductions[nrdeds] := [triple[1][app[1]],app[2]];
- fi;
- od;
-
- ded := ded + 1;
- od;
-
- fi;
- od;
-
- cos := cos + 1;
- od;
-
- # return the generators
- return gens;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Order(<G>,<w>) . . . order of an element in a fin. pres. group
- ##
- FpGroupOps.Order := function ( G, w )
- local ord, # order of <w>, result
- table, # coset table of the trivial subgroup of <G>
- g, # one generator of <G>
- c, # coset in tracing
- i; # loop variable
-
- # trace the word through the coset table of the identity until we hit 1
- G := Parent( G );
- if Size( G ) = "infinity" then
- Error("sorry, cannot find the order of <w> in the infinite group <G>");
- fi;
- table := G.trivialSubgroup.cosetTable;
- c := 1;
- ord := 0;
- repeat
- for i in [ 1 .. LengthWord( w ) ] do
- g := Subword( w, i, i );
- if g in G.generators then
- c := table[ 2*Position(G.generators,g)-1 ][ c ];
- elif g^-1 in G.generators then
- c := table[ 2*Position(G.generators,g^-1) ][ c ];
- else
- Error("<w> must lie in <G>");
- fi;
- od;
- ord := ord + 1;
- until c = 1;
- return ord;
-
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Closure(<G>,<g>) . closure of a subgroup in a fin. pres. group
- ##
- FpGroupOps.Closure := function ( G, w )
- local C, # closure of <G> and <w>, result
- g; # one generator
-
- # closure with the parent
- if IsParent( G ) then
- return G;
- fi;
-
- # handle the closure of a subgroup with another subgroup
- if IsGroup( w ) then
- C := G;
- for g in w.generators do
- C := Closure( C, g );
- od;
- return C;
- fi;
-
- # if possible test if the element lies in the group already
- if IsBound( G.cosetTable ) and w in G then
- return G;
- fi;
-
- # otherwise make a new group
- C := Subgroup( Parent( G ), Concatenation( G.generators, [ w ] ) );
-
- # return the closure
- return C;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.Normalizer(<G>,<H>) . . . . normalizer in a fin. pres. group
- ##
- FpGroupOps.Normalizer := function ( G, H )
- local N, # normalizer of <H> in <G>, result
- table, # coset table of <H> in its parent
- nrcos, # number of cosets in the table
- nrgens, # 2*(number of generators of <H>s parent)+1
- iseql, # true if coset <c> normalizes <H>
- r, s, # renumbering of the coset table and its inverse
- c, d, e, # coset loop variables
- g, h; # generator loop variables
-
- # handle the case the <H> is contained in <G>
- if IsParent( G ) or IsSubgroup( G, H ) then
-
- # first we need the coset table of <H>
- if not IsBound( H.cosetTable ) then
- H.cosetTable := CosetTableFpGroup( Parent( H ), H );
- fi;
- table := H.cosetTable;
- nrcos := Length( table[1] );
- nrgens := 2*Length( Parent(H).generators ) + 1;
-
- # find the cosets of <H> in its parent whose elements normalize <H>
- N := H;
- for c in [ 2 .. nrcos ] do
-
- # test if the renumbered table is equal to the original table
- r := 0 * [ 1 .. nrcos ];
- s := 0 * [ 1 .. nrcos ];
- r[c] := 1; s[1] := c;
- e := 1;
- iseql := true;
- d := 1;
- while d <= nrcos and iseql do
- g := 1;
- while g < nrgens and iseql do
- if r[ table[g][s[d]] ] = 0 then
- e := e + 1;
- r[ table[g][s[d]] ] := e;
- s[ e ] := table[g][s[d]];
- fi;
- iseql := (r[ table[g][s[d]] ] = table[g][d]);
- g := g + 2;
- od;
- d := d + 1;
- od;
-
- # add the representative of this coset if it normalizes
- if iseql then
- r := IdWord;
- d := c;
- while d <> 1 do
- g := nrgens - 1;
- h := nrgens - 1;
- while 0 < g do
- if table[g][d] <= table[h][d] then
- h := g;
- fi;
- g := g - 2;
- od;
- r := Parent( H ).generators[ h/2 ] * r;
- d := table[h][d];
- od;
- if r in G and not r in N then
- N := Closure( N, r );
- fi;
- fi;
-
- od;
-
- # delegate other cases
- else
-
- N := GroupOps.Normalizer( G, H );
-
- fi;
-
- # return the normalizer
- return N;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.IsAbelian( <G> ) . . . . test if a fin. pres. group is abelian
- ##
- FpGroupOps.IsAbelian := function ( G )
- local isAbelian, # result
- g, h, # two generators of <G>
- i, k; # loop variables
- isAbelian := true;
- for i in [ 1 .. Length( G.generators ) - 1 ] do
- g := G.generators[i];
- for k in [ i + 1 .. Length( G.generators ) ] do
- h := G.generators[k];
- isAbelian := isAbelian
- and (Comm( g, h ) in G.relators
- or Comm( g, h ) in TrivialSubgroup( G ));
- od;
- od;
- return isAbelian;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.CommutatorFactorGroup( <G> ) . . . . . commutator factor group
- #F of a fin. pres. group
- ##
- FpGroupOps.CommutatorFactorGroup := function ( G )
- local C, # commutator factor group of <G>, result
- gens, # generators of <C>
- rels, # relators of <C>
- rel, # one relation of <C>
- old, # one relation of <G>
- g, h, # two generators of <G> or <C>
- i, k; # loop variables
-
- # we can handle only groups with relators
- if not IsParent( G ) then
- G := FpGroup( G );
- fi;
-
- # make a new set of generators
- gens := [];
- for i in [ 1 .. Length( G.generators ) ] do
- gens[i] := AbstractGenerator(
- ConcatenationString( "c.", String( i ) )
- );
- od;
-
- # make the relators
- rels := [];
- for old in G.relators do
- rel := IdWord;
- for i in [ 1 .. LengthWord( old ) ] do
- g := Subword( old, i, i );
- if g in G.generators then
- rel := rel * gens[ Position( G.generators, g ) ];
- else
- rel := rel * gens[ Position( G.generators, g^-1 ) ]^-1;
- fi;
- od;
- Add( rels, rel );
- od;
-
- # add the commutator relators
- for i in [ 1 .. Length( gens ) - 1 ] do
- g := gens[i];
- for k in [ i + 1 .. Length( gens ) ] do
- h := gens[k];
- if not Comm( g, h ) in rels then
- Add( rels, Comm( g, h ) );
- fi;
- od;
- od;
-
- # make the commutator factor group and return it
- C := Group( gens, IdWord );
- C.relators := rels;
- C.isAbelian := true;
- return C;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.AbelianInvariants(<G>) . . . abelian invariants of an abelian
- #F fin. pres. group
- ##
- FpGroupOps.AbelianInvariants := function ( G )
-
- local abl, # abelian invariants of <G>, result
- mat, # relation matrix of <G>
- row, # one row of <mat>
- rel, # one relation of <G>
- g, # one letter of <rel>
- p, # position of <g> or its inverse in '<G>.generators'
- i, # loop variable
- divs, # elementary divisors
- gcd, # extended gcd
- m, n, k;
-
- # we can handle only groups with relators
- if not IsParent( G ) then
- G := FpGroup( G );
- fi;
-
- # make the relation matrix
- mat := [];
- for rel in G.relators do
- row := [];
- for i in [ 1 .. Length( G.generators ) ] do
- row[i] := 0;
- od;
- for i in [ 1 .. LengthWord( rel ) ] do
- g := Subword( rel, i, i );
- p := Position( G.generators, g );
- if p <> false then
- row[ p ] := row[ p ] + 1;
- else
- p := Position( G.generators, g^-1 );
- row[ p ] := row[ p ] - 1;
- fi;
- od;
- Add( mat, row );
- od;
-
- # diagonalize the matrix
- DiagonalizeMat( mat );
-
- # get the diagonal elements
- m := Length(mat); n := Length(mat[1]);
- divs := [];
- for i in [1..Minimum(m,n)] do
- divs[i] := mat[i][i];
- od;
-
- # transform the divisors so that every divisor divides the next
- for i in [1..Length(divs)-1] do
- for k in [i+1..Length(divs)] do
- if divs[i] <> 0 and divs[k] mod divs[i] <> 0 then
- gcd := GcdInt( divs[i], divs[k] );
- divs[k] := divs[k] / gcd * divs[i];
- divs[i] := gcd;
- fi;
- od;
- od;
-
- # and return the ablian invariants
- abl := [];
- for i in divs do
- if i <> 1 then
- Add( abl, i );
- fi;
- od;
- return abl;
- end;
-
-
- #############################################################################
- ##
- #F OperationCosetsFpGroup(<G>,<H>) . . . . . . . . . operation on the cosets
- ##
- OperationCosetsFpGroup := function ( G, H )
- local P, # permutation group, result
- gens, # generators of <P>
- i; # loop variable
-
- # check the arguments
- if not IsParent( G ) or G <> Parent( H ) then
- Error("<G> must be the parent group of <H>");
- fi;
-
- # first we need the coset table of <H>
- if not IsBound( H.cosetTable ) then
- H.cosetTable := CosetTableFpGroup( G, H );
- fi;
-
- # now make the permutation group
- gens := [];
- for i in [1..Length(H.cosetTable)/2] do
- Add( gens, PermList( H.cosetTable[2*i-1] ) );
- od;
- P := Group( gens, () );
- P.operationGroup := G;
- P.operationDomain := H;
- P.operationOperation := "OperationCosetsFpGroup";
- P.operationImages := gens;
-
- # return the permutation group
- return P;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.OperationHomomorphism(<G>,<P>) . . . . operation homomorphism
- #F from a finitely presented group
- ##
- FpGroupOps.OperationHomomorphism := function ( G, P )
- local hom;
- if P.operationOperation = "OperationCosetsFpGroup" then
- hom := GroupHomomorphismByImages( G, P,
- G.generators, P.operationImages );
- hom.isMapping := true;
- else
- hom := GroupOps.OperationsHomomorphism( G, P );
- fi;
- return hom;
- end;
-
-
- #############################################################################
- ##
- #F FpGroupOps.GroupHomomorphismByImages(<G>,<H>,<gens>,<imgs>) . . . create
- #F a finitely presented group homomorphism by images of a generating system
- ##
- FpGroupHomomorphismByImagesOps := Copy( GroupHomomorphismByImagesOps );
-
- FpGroupOps.GroupHomomorphismByImages := function ( G, H, gens, imgs )
- local hom; # homomorphism from <G> to <H>, result
-
- # check that we can handle the situation
- if Set( gens ) <> Set( G.generators ) then
- Error("arbitrary generating systems not yet allowed for fp groups");
- fi;
-
- # make the homomorphism
- hom := rec();
- hom.isGeneralMapping := true;
- hom.domain := Mappings;
-
- # enter the identifying information
- hom.source := G;
- hom.range := H;
- hom.generators := gens;
- hom.genimages := imgs;
-
- # enter usefull information (precious little)
- if IsEqualSet( gens, G.generators ) then
- hom.preimage := G;
- else
- hom.preimage := Parent(G).operations.Subgroup( Parent(G), gens );
- fi;
- if IsSubsetSet( imgs, H.generators ) then
- hom.image := H;
- else
- hom.image := Parent(H).operations.Subgroup( Parent(H), imgs );
- fi;
-
- # enter the operations record
- hom.operations := FpGroupHomomorphismByImagesOps;
-
- # return the homomorphism
- return hom;
- end;
-
- FpGroupHomomorphismByImagesOps.CoKernel := function ( hom )
- local C;
-
- C := NormalClosure( hom.image,
- Subgroup( Parent( hom.image ),
- List( hom.source.relators,
- rel -> MappedWord( rel,
- hom.generators,
- hom.genimages))));
-
- return C;
- end;
-
- FpGroupHomomorphismByImagesOps.IsMapping := function ( hom )
- return hom.source = hom.preimage
- and ForAll( hom.source.relators,
- rel -> MappedWord( rel, hom.generators, hom.genimages )
- = hom.range.identity );
- end;
-
- FpGroupHomomorphismByImagesOps.IsGroupHomomorphism := function ( hom )
- return IsMapping( hom );
- end;
-
- FpGroupHomomorphismByImagesOps.ImageElm := function ( hom, elm )
- if not IsMapping( hom ) then
- Error("<hom> must be a single valued mapping");
- fi;
- return MappedWord( elm, hom.generators, hom.genimages );
- end;
-
- FpGroupHomomorphismByImagesOps.ImagesElm := function ( hom, elm )
- if not IsBound( hom.coKernel ) then
- hom.coKernel := hom.operations.CoKernel( hom );
- fi;
- return hom.coKernel * MappedWord( elm, hom.generators, hom.genimages );
- end;
-
- FpGroupHomomorphismByImagesOps.ImagesSet := function ( hom, elms )
- if IsGroup( elms ) and IsSubset( hom.source, elms ) then
- if not IsBound( hom.coKernel ) then
- hom.coKernel := hom.operations.CoKernel( hom );
- fi;
- return Closure( hom.coKernel,
- Parent( hom.range ).operations.Subgroup(
- Parent( hom.range ),
- List( elms.generators,
- gen -> MappedWord( gen,
- hom.generators,
- hom.genimages))));
- else
- return GroupHomomorphismOps.ImagesSet( hom, elms );
- fi;
- end;
-
- FpGroupHomomorphismByImagesOps.ImagesRepresentative := function ( hom, elm )
- return MappedWord( elm, hom.generators, hom.genimages );
- end;
-
- FpGroupHomomorphismByImagesOps.CompositionMapping := function ( hom1, hom2 )
- local prd; # product of <hom1> and <hom2>, result
-
- # product of a homomorphism by generator images
- if IsHomomorphism( hom2 ) and IsBound( hom2.genimages ) then
-
- # with another homomorphism
- if IsHomomorphism( hom1 ) then
-
- # just do it
- prd := GroupHomomorphismByImages(
- hom2.source,
- hom1.range,
- hom2.generators,
- List( hom2.genimages, img -> Image( hom1, img ) ) );
-
- # with another mapping
- else
-
- prd := MappingOps.CompositionMapping( hom1, hom2 );
-
- fi;
-
- # of something else
- else
- prd := MappingOps.CompositionMapping( hom1, hom2 );
- fi;
-
- # return the product
- return prd;
- end;
-
- FpGroupHomomorphismByImagesOps.Print := function ( hom )
- Print( "GroupHomomorphismByImages( ",
- hom.source, ", ", hom.range, ", ",
- hom.generators, ", ", hom.genimages, " )" );
- end;
-
-
- #############################################################################
- ##
- #F LowIndexSubgroupsFpGroup(<G>,<index>) . find all subgroups of small index
- #F in a fin. pres. group
- ##
- LowIndexSubgroupsFpGroup := function ( G, H, index )
- local subs, # subgroups of <G>, result
- sub, # one subgroup
- gens, # generators of <sub>
- table, # coset table
- nrgens, # 2*(number of generators)+1
- nrcos, # number of cosets in the coset table
- action, # 'action[<i>]' is "definition" or "choice" or "ded"
- actgen, # 'actgen[<i>]' is the gen where this action was
- actcos, # 'actcos[<i>]' is the coset where this action was
- nract, # number of actions
- nrded, # number of deductions already handled
- coinc, # 'true' if a coincidence happened
- gen, # current generator
- cos, # current coset
- relsGen, # relators sorted by start generator
- subgroup, # rows for the subgroup gens
- app, # arguments list for 'ApplyRel'
- later, # 'later[<i>]' is <> 0 if <i> is smaller than 1
- nrfix, # index of a subgroup in its normalizer
- pair, # loop variable for subgroup generators as pairs
- rel, # loop variable for relators
- triple, # loop variable for relators as triples
- r, s, x, y, # loop variables
- g, c, d, # loop variables
- p, p1, p2, # generator position numbers
- length, # relator length
- length2, # twice a relator length
- cols,
- gen,
- nums,
- i, j; # loop variables
-
- # give some information
- InfoFpGroup1("#I LowIndexSubgroupsFpGroup called\n");
-
- # check the arguments
- if not IsParent( G ) or G <> Parent( H ) then
- Error("<G> must be the parent group of <H>");
- fi;
-
- # initialize the subgroup list
- subs := [];
-
- # initialize table
- nrgens := 2*Length(G.generators)+1;
- nrcos := 1;
- table := [];
- for gen in G.generators do
- g := 0*[1..index];
- Add( table, g );
- if not ( gen^2 in G.relators or gen^-2 in G.relators ) then
- g := 0*[1..index];
- fi;
- Add( table, g );
- od;
-
- # make the rows for the relators and distribute over relsGen
- relsGen := RelsSortedByStartGen( G, table );
-
- # make the rows for the subgroup generators
- subgroup := [];
- for rel in H.generators do
- length := LengthWord( rel );
- length2 := 2 * length;
- nums := 0 * [1 .. length2];
- cols := 0 * [1 .. length2];
-
- # compute the lists.
- i := 0; j := 0;
- while i < length do
- i := i + 1; j := j + 2;
- gen := Subword( rel, i, i );
- p := Position( G.generators, gen );
- if p = false then
- p := Position( G.generators, gen^-1 );
- p1 := 2 * p;
- p2 := 2 * p - 1;
- else
- p1 := 2 * p - 1;
- p2 := 2 * p;
- fi;
- nums[j] := p1; cols[j] := table[p1];
- nums[j-1] := p2; cols[j-1] := table[p2];
- od;
- Add( subgroup, [ nums, cols ] );
- od;
-
- # make an structure that is passed to 'ApplyRel'
- app := 0 * [ 1 .. 4 ];
-
- # set up the action stack
- nract := 1;
- action := [ "choice" ];
- gen := 1;
- actgen := [ gen ];
- cos := 1;
- actcos := [ cos ];
-
- # set up the lexicographical information list
- later := 0 * [1..index];
-
- # do an exhaustive backtrack search
- while 1 < nract or table[1][1] < 2 do
-
- # find the next choice that does not already appear in this col.
- c := table[ gen ][ cos ];
- repeat
- c := c + 1;
- until index < c or table[ gen+1 ][ c ] = 0;
-
- # if there is a further choice try it
- if action[nract] <> "definition" and c <= index then
-
- # remove the last choice from the table
- d := table[ gen ][ cos ];
- if d <> 0 then
- table[ gen+1 ][ d ] := 0;
- fi;
-
- # enter it in the table
- table[ gen ][ cos ] := c;
- table[ gen+1 ][ c ] := cos;
-
- # and put information on the action stack
- if c = nrcos + 1 then
- nrcos := nrcos + 1;
- action[ nract ] := "definition";
- else
- action[ nract ] := "choice";
- fi;
-
- # run through the deduction queue until it is empty
- nrded := nract;
- coinc := false;
- while nrded <= nract and not coinc do
-
- # if there are still subgroup generators apply them
- for pair in subgroup do
- app[1] := 2;
- app[2] := 1;
- app[3] := Length(pair[2])-1;
- app[4] := 1;
- if ApplyRel( app, pair[2] ) then
- if pair[2][app[1]][app[2]] <> 0 then
- coinc := true;
- elif pair[2][app[3]][app[4]] <> 0 then
- coinc := true;
- else
- pair[2][app[1]][app[2]] := app[4];
- pair[2][app[3]][app[4]] := app[2];
- nract := nract + 1;
- action[ nract ] := "deduction";
- actgen[ nract ] := pair[1][app[1]];
- actcos[ nract ] := app[2];
- fi;
- fi;
- od;
-
- # apply all relators that start with this generator
- for triple in relsGen[actgen[nrded]] do
- app[1] := triple[3];
- app[2] := actcos[ nrded ];
- app[3] := -1;
- app[4] := app[2];
- if ApplyRel( app, triple[2] ) then
- if triple[2][app[1]][app[2]] <> 0 then
- coinc := true;
- elif triple[2][app[3]][app[4]] <> 0 then
- coinc := true;
- else
- triple[2][app[1]][app[2]] := app[4];
- triple[2][app[3]][app[4]] := app[2];
- nract := nract + 1;
- action[ nract ] := "deduction";
- actgen[ nract ] := triple[1][app[1]];
- actcos[ nract ] := app[2];
- fi;
- fi;
- od;
-
- nrded := nrded + 1;
- od;
-
- # unless there was a coincidence check lexicography
- nrfix := 1;
- for x in [2..nrcos] do
-
- # set up the renumbering
- r := 0 * [1..nrcos];
- s := 0 * [1..nrcos];
- r[x] := 1; s[1] := x;
-
- # run through the old and the new table in parallel
- c := 1; y := 1;
- while c <= nrcos and not coinc and later[x] = 0 do
-
- # get the corresponding coset for the new table
- d := s[c];
-
- # loop over the entries in this row
- g := 1;
- while g < nrgens
- and c <= nrcos and not coinc and later[x] = 0 do
-
- # if either entry is missing we cannot decide yet
- if table[g][c] = 0 or table[g][d] = 0 then
- c := nrcos + 1;
-
- # if old and new both contain a definition
- elif r[ table[g][d] ] = 0 and table[g][c] = y+1 then
- y := y + 1;
- r[ table[g][d] ] := y;
- s[ y ] := table[g][d];
-
- # if only new is a definition
- elif r[ table[g][d] ] = 0 then
- later[x] := nract;
-
- # if new is the smaller one we have a coincidence
- elif r[ table[g][d] ] < table[g][c] then
- #N 05-Feb-91 martin check that <x> fixes <H>
- coinc := true;
-
- # if the old is smaller one very good
- elif table[g][c] < r[ table[g][d] ] then
- later[x] := nract;
-
- fi;
-
- g := g + 2;
- od;
-
- c := c + 1;
- od;
-
- if c = nrcos + 1 then
- nrfix := nrfix + 1;
- fi;
-
- od;
-
- # if there was no coincidence
- if not coinc then
-
- # look for another empty place
- c := cos;
- g := gen;
- while c <= nrcos and table[ g ][ c ] <> 0 do
- g := g + 2;
- if g = nrgens then
- c := c + 1;
- g := 1;
- fi;
- od;
-
- # if there is an empty place, make this a new choice point
- if c <= nrcos then
-
- nract := nract + 1;
- action[ nract ] := "choice"; # necessary?
- gen := g;
- actgen[ nract ] := gen;
- cos := c;
- actcos[ nract ] := cos;
- table[ gen ][ cos ] := 0; # necessary?
-
- # otherwise we found a subgroup
- else
-
- # give some information
- InfoFpGroup2( "#I class ", Length(subs)+1,
- " of index ", nrcos,
- " and length ", nrcos / nrfix, "\n" );
-
- # find a generating system for the subgroup
- gens := [];
- for i in [ 1 .. nract ] do
- if action[ i ] = "choice" then
- x := IdWord;
- c := actcos[i];
- while c <> 1 do
- g := nrgens - 1;
- y := nrgens - 1;
- while 0 < g do
- if table[g][c] <= table[y][c] then
- y := g;
- fi;
- g := g - 2;
- od;
- x := G.generators[ y/2 ] * x;
- c := table[y][c];
- od;
- x := x * G.generators[ (actgen[i]+1)/2 ];
- c := table[ actgen[i] ][ actcos[i] ];
- while c <> 1 do
- g := nrgens - 1;
- y := nrgens - 1;
- while 0 < g do
- if table[g][c] <= table[y][c] then
- y := g;
- fi;
- g := g - 2;
- od;
- x := x * G.generators[ y/2 ]^-1;
- c := table[y][c];
- od;
- Add( gens, x );
- fi;
- od;
-
- # add the coset table
- sub := Subgroup( Parent( G ), gens );
- sub.cosetTable := [];
- for g in [ 1 .. Length( G.generators ) ] do
- sub.cosetTable[2*g-1]
- := Sublist( table[2*g-1], [1..nrcos] );
- if G.generators[g]^2 in G.relators
- or G.generators[g]^-2 in G.relators
- then
- sub.cosetTable[2*g]
- := sub.cosetTable[2*g-1];
- else
- sub.cosetTable[2*g]
- := Sublist( table[2*g], [1..nrcos] );
- fi;
- od;
-
- # add this subgroup to the list of subgroups
- #N 05-Feb-92 martin should be 'ConjugacyClassSubgroup'
- Add( subs, sub );
-
- # undo all deductions since the previous choice point
- while action[ nract ] = "deduction" do
- g := actgen[ nract ];
- c := actcos[ nract ];
- d := table[ g ][ c ];
- if g mod 2 = 1 then
- table[ g ][ c ] := 0;
- table[ g+1 ][ d ] := 0;
- else
- table[ g ][ c ] := 0;
- table[ g-1 ][ d ] := 0;
- fi;
- nract := nract - 1;
- od;
- for x in [2..index] do
- if nract <= later[x] then
- later[x] := 0;
- fi;
- od;
-
- fi;
-
- # if there was a coincendence go back to the current choice point
- else
-
- # undo all deductions since the previous choice point
- while action[ nract ] = "deduction" do
- g := actgen[ nract ];
- c := actcos[ nract ];
- d := table[ g ][ c ];
- if g mod 2 = 1 then
- table[ g ][ c ] := 0;
- table[ g+1 ][ d ] := 0;
- else
- table[ g ][ c ] := 0;
- table[ g-1 ][ d ] := 0;
- fi;
- nract := nract - 1;
- od;
- for x in [2..index] do
- if nract <= later[x] then
- later[x] := 0;
- fi;
- od;
-
- fi;
-
- # go back to the previous choice point if there are no more choices
- else
-
- # undo the choice point
- if action[ nract ] = "definition" then
- nrcos := nrcos - 1;
- fi;
- g := actgen[ nract ];
- c := actcos[ nract ];
- d := table[ g ][ c ];
- if g mod 2 = 1 then
- table[ g ][ c ] := 0;
- table[ g+1 ][ d ] := 0;
- else
- table[ g ][ c ] := 0;
- table[ g-1 ][ d ] := 0;
- fi;
- nract := nract - 1;
-
- # undo all deductions since the previous choice point
- while action[ nract ] = "deduction" do
- g := actgen[ nract ];
- c := actcos[ nract ];
- d := table[ g ][ c ];
- if g mod 2 = 1 then
- table[ g ][ c ] := 0;
- table[ g+1 ][ d ] := 0;
- else
- table[ g ][ c ] := 0;
- table[ g-1 ][ d ] := 0;
- fi;
- nract := nract - 1;
- od;
- for x in [2..index] do
- if nract <= later[x] then
- later[x] := 0;
- fi;
- od;
-
- cos := actcos[ nract ];
- gen := actgen[ nract ];
-
- fi;
-
- od;
-
- # give some final information
- InfoFpGroup1("#I LowIndexSubgroupsFpGroup returns ",
- Length(subs), " classes\n" );
-
- # return the subgroups
- return subs;
- end;
-
-
- #############################################################################
- ##
- #R Read . . . . . . . . . . . . . read other function from the other files
- ##
- ReadLib( "fptietze" );
- ReadLib( "fpsgpres" );
-
-
- #############################################################################
- ##
- #E Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
- ##
- ## Local Variables:
- ## mode: outline
- ## outline-regexp: "#F\\|#V\\|#E"
- ## fill-column: 73
- ## fill-prefix: "## "
- ## eval: (hide-body)
- ## End:
- ##
-
-
-
-