Skip to content

Commit

Permalink
Unify argument checking for some free objects
Browse files Browse the repository at this point in the history
In particular, unify argument processing/error checking in:
* `FreeSemigroup`
* `FreeMonoid`
* `FreeMagma`
* `FreeMagmaWithOne`
* `FreeGroup`

These now use the newly-added function `FreeXArgumentProcessor`.
Their documentation has also been updated, and tests added.

Resolves #1385.
  • Loading branch information
wilfwilson committed Jun 11, 2021
1 parent 48c4b04 commit 497bc4f
Show file tree
Hide file tree
Showing 14 changed files with 1,212 additions and 596 deletions.
14 changes: 8 additions & 6 deletions doc/ref/grplib.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,14 @@ calculations.

<Subsection Label="Generator Names">
<Heading>Generator Names</Heading>
For groups created as finitely presented groups, including polycyclic groups, the
generators are labelled, by default, with a letter and a number. Using the
option <C>generatorNames</C> it is possible to influcence this naming. If
this option holds a string, the generators are named with this string and
numbers, if a list of strings of sufficient length is given, the generator
names are taken from this list of strings.
For groups created as finitely presented groups, including polycyclic groups,
the generators are labelled, by default, with a letter and a number.
It is possible to influence this naming with the option <C>generatorNames</C>,
see Section&nbsp;<Ref Sect="Function Call With Options"/>.
If this option holds a nonempty string, then the generators are named with this
string and sequential numbers starting with <C>1</C>.
If this option holds a list of sufficient length consisting of
nonempty strings, then the generator names are taken from this list, in order.
<P/>
<Example><![CDATA[
gap> GeneratorsOfGroup(AbelianGroup([5,7]));
Expand Down
136 changes: 99 additions & 37 deletions lib/grpfree.gd
Original file line number Diff line number Diff line change
Expand Up @@ -32,62 +32,124 @@ DeclareSynonym( "IsElementOfFreeGroupFamily",IsAssocWordWithInverseFamily );

#############################################################################
##
#F FreeGroup( [<wfilt>,]<rank> )
#F FreeGroup( [<wfilt>,]<rank>, <name> )
#F FreeGroup( [<wfilt>,]<name1>, <name2>, ... )
#F FreeGroup( [<wfilt>,]<rank>[, <name>] )
#F FreeGroup( [<wfilt>,][<name1>[, <name2>[, ...]]] )
#F FreeGroup( [<wfilt>,]<names> )
#F FreeGroup( [<wfilt>,]infinity, <name>, <init> )
#F FreeGroup( [<wfilt>,]infinity[, <name>][, <init>] )
##
## <#GAPDoc Label="FreeGroup">
## <ManSection>
## <Heading>FreeGroup</Heading>
## <Func Name="FreeGroup" Arg='[wfilt, ]rank[, name]'
## Label="for given rank"/>
## <Func Name="FreeGroup" Arg='[wfilt, ]name1, name2, ...'
## <Func Name="FreeGroup" Arg='[wfilt, ][name1[, name2[, ...]]]'
## Label="for various names"/>
## <Func Name="FreeGroup" Arg='[wfilt, ]names'
## Label="for a list of names"/>
## <Func Name="FreeGroup" Arg='[wfilt, ]infinity, name, init'
## <Func Name="FreeGroup" Arg='[wfilt, ]infinity[, name][, init]'
## Label="for infinitely many generators"/>
##
## <Description>
## Called with a positive integer <A>rank</A>,
## <Ref Func="FreeGroup" Label="for given rank"/> returns
## a free group on <A>rank</A> generators.
## If the optional argument <A>name</A> is given then the generators are
## printed as <A>name</A><C>1</C>, <A>name</A><C>2</C> etc.,
## that is, each name is the concatenation of the string <A>name</A> and an
## integer from <C>1</C> to <A>range</A>.
## The default for <A>name</A> is the string <C>"f"</C>.
## <P/>
## Called in the second form,
## <Ref Func="FreeGroup" Label="for various names"/> returns
## a free group on as many generators as arguments, printed as
## <A>name1</A>, <A>name2</A> etc.
## <P/>
## Called in the third form,
## <Ref Func="FreeGroup" Label="for a list of names"/> returns
## a free group on as many generators as the length of the list
## <A>names</A>, the <M>i</M>-th generator being printed as
## <A>names</A><C>[</C><M>i</M><C>]</C>.
## <P/>
## Called in the fourth form,
## <Ref Func="FreeGroup" Label="for infinitely many generators"/>
## returns a free group on infinitely many generators, where the first
## generators are printed by the names in the list <A>init</A>,
## and the other generators by <A>name</A> and an appended number.
## <P/>
## If the extra argument <A>wfilt</A> is given, it must be either
## <C>IsSyllableWordsFamily</C> or <C>IsLetterWordsFamily</C> or
## <C>IsWLetterWordsFamily</C> or <C>IsBLetterWordsFamily</C>.
## This filter then specifies the representation used for the elements of
## <C>FreeGroup</C> returns a free group. The number of
## generators, and the labels given to the generators, can be specified in
## several different ways.
## Warning: the labels of generators are only an aid for printing,
## and do not necessarily distinguish generators;
## see the examples at the end of
## <Ref Func="FreeSemigroup" Label="for given rank"/>
## for more information.
## <List>
## <Mark>
## 1: For a given rank, and an optional generator name prefix
## </Mark>
## <Item>
## Called with a nonnegative integer <A>rank</A>,
## <Ref Func="FreeGroup" Label="for given rank"/> returns
## a free group on <A>rank</A> generators.
## The optional argument <A>name</A> must be a string;
## its default value is <C>"f"</C>. <P/>
##
## If <A>name</A> is not given but the <C>generatorNames</C> option is,
## then this option is respected as described in
## Section&nbsp;<Ref Sect="Generator Names"/>. <P/>
##
## Otherwise, the generators of the returned free group are labelled
## <A>name</A><C>1</C>, ..., <A>name</A><C>k</C>,
## where <C>k</C> is the value of <A>rank</A>. <P/>
## </Item>
## <Mark>2: For given generator names</Mark>
## <Item>
## Called with various nonempty strings,
## <Ref Func="FreeGroup" Label="for various names"/> returns
## a free group on as many generators as arguments, which are labelled
## <A>name1</A>, <A>name2</A>, etc.
## </Item>
## <Mark>3: For a given list of generator names</Mark>
## <Item>
## Called with a finite list <A>names</A> of
## nonempty strings,
## <Ref Func="FreeGroup" Label="for a list of names"/> returns
## a free group on <C>Length(<A>names</A>)</C> generators, whose
## <C>i</C>-th generator is labelled <A>names</A><C>[i]</C>.
## </Item>
## <Mark>
## 4: For the rank <K>infinity</K>,
## an optional default generator name prefix,
## and an optional finite list of generator names
## </Mark>
## <Item>
## Called in the fourth form,
## <Ref Func="FreeGroup" Label="for infinitely many generators"/>
## returns a free group on infinitely many generators.
## The optional argument <A>name</A> must be a string; its default value is
## <C>"f"</C>,
## and the optional argument <A>init</A> must be a finite list of
## nonempty strings; its default value is an empty list.
## The generators are initially labelled according to the list <A>init</A>,
## followed by
## <A>name</A><C>i</C> for each <C>i</C> in the range from
## <C>Length(<A>init</A>)+1</C> to <K>infinity</K>.
## </Item>
## </List>
## If the optional first argument <A>wfilt</A> is given, then it must be either
## <C>IsSyllableWordsFamily</C>, <C>IsLetterWordsFamily</C>,
## <C>IsWLetterWordsFamily</C>, or <C>IsBLetterWordsFamily</C>.
## This filter specifies the representation used for the elements of
## the free group
## (see&nbsp;<Ref Sect="Representations for Associative Words"/>).
## If no such filter is given, a letter representation is used.
## <P/>
## (For interfacing to old code that omits the representation flag, use of
## the syllable representation is also triggered by setting the option
## <C>FreeGroupFamilyType</C> to the string <C>"syllable"</C>.)
## <C>FreeGroupFamilyType</C> to the string <C>"syllable"</C>; this is
## overwritten by the optional first argument if it is given.)
##
## <Example><![CDATA[
## gap> FreeGroup(5);
## <free group on the generators [ f1, f2, f3, f4, f5 ]>
## gap> FreeGroup(4, "gen");
## <free group on the generators [ gen1, gen2, gen3, gen4 ]>
## gap> FreeGroup(3 : generatorNames := "ack");
## <free group on the generators [ ack1, ack2, ack3 ]>
## gap> FreeGroup(2 : generatorNames := ["u", "v", "w"]);
## <free group on the generators [ u, v ]>
## gap> FreeGroup();
## <free group of rank zero>
## gap> FreeGroup("a", "b", "c");
## <free group on the generators [ a, b, c ]>
## gap> FreeGroup(["x", "y"]);
## <free group on the generators [ x, y ]>
## gap> FreeGroup(infinity);
## <free group with infinity generators>
## gap> F := FreeGroup(infinity, "g", ["a", "b"]);
## <free group with infinity generators>
## gap> GeneratorsOfGroup(F){[1..4]};
## [ a, b, g3, g4 ]
## gap> GeneratorsOfGroup(FreeGroup(infinity, "gen")){[1..3]};
## [ gen1, gen2, gen3 ]
## gap> FreeGroup(IsSyllableWordsFamily, 50);
## <free group with 50 generators>
## ]]></Example>
## </Description>
## </ManSection>
## <#/GAPDoc>
Expand Down
110 changes: 23 additions & 87 deletions lib/grpfree.gi
Original file line number Diff line number Diff line change
Expand Up @@ -416,117 +416,53 @@ InstallMethod( GeneratorsSmallest,

#############################################################################
##
#F FreeGroup( <rank> ) . . . . . . . . . . . . . . free group of given rank
#F FreeGroup( <rank>, <name> )
#F FreeGroup( <name1>, <name2>, ... )
#F FreeGroup( <names> )
#F FreeGroup( infinity, <name>, <init> )
#F FreeGroup( [<wfilt>,]<rank>[, <name>] ) . . . . free group of given rank
#F FreeGroup( [<wfilt>,][<name1>[, <name2>[, ...]]] )
#F FreeGroup( [<wfilt>,]<names> )
#F FreeGroup( [<wfilt>,]infinity[, <name>][, <init>] )
##
InstallGlobalFunction( FreeGroup, function ( arg )
local names, # list of generators names
opt,
zarg,
lesy, # filter for letter or syllable words family
local rank, # number of generators
F, # family of free group element objects
G; # free group, result
G, # free group, result
processed;

if ValueOption("FreeGroupFamilyType")="syllable" then
lesy:=IsSyllableWordsFamily; # optional -- used in PQ
else
lesy:=IsLetterWordsFamily; # default
fi;
if IsFilter(arg[1]) then
lesy:=arg[1];
zarg:=arg{[2..Length(arg)]};
else
zarg:=arg;
fi;

# Get and check the argument list, and construct names if necessary.
if Length( zarg ) = 1 and zarg[1] = infinity then
names:= InfiniteListOfNames( "f" );
elif Length( zarg ) = 2 and zarg[1] = infinity then
names:= InfiniteListOfNames( zarg[2] );
elif Length( zarg ) = 3 and zarg[1] = infinity then
names:= InfiniteListOfNames( zarg[2], zarg[3] );
elif Length( zarg ) = 1 and IsInt( zarg[1] ) and 0 <= zarg[1] then
names:= List( [ 1 .. zarg[1] ],
i -> Concatenation( "f", String(i) ) );
MakeImmutable( names );
elif Length( zarg ) = 2 and IsInt( zarg[1] ) and 0 <= zarg[1] then
names:= List( [ 1 .. zarg[1] ],
i -> Concatenation( zarg[2], String(i) ) );
elif Length( zarg ) = 1 and IsList( zarg[1] ) and IsEmpty( zarg[1] ) then
names:= zarg[1];
elif 1 <= Length( zarg ) and ForAll( zarg, IsString ) then
names:= zarg;
elif Length( zarg ) = 1 and IsList( zarg[1] )
and ForAll( zarg[1], IsString ) then
names:= zarg[1];
else
Error("usage: FreeGroup(<name1>,<name2>..) or FreeGroup(<rank>)");
fi;

opt:=ValueOption("generatorNames");
if opt<>fail then
if IsString(opt) then
names:= List( [ 1 .. Length(names) ],
i -> Concatenation( opt, String(i) ) );
elif IsList(opt) and ForAll(opt,IsString)
and Length(names)<=Length(opt) then
names:=opt{[1..Length(names)]};
MakeImmutable( names );
else
Error("Cannot process `generatorNames` option");
fi;
fi;

# deal with letter words family types
if lesy=IsLetterWordsFamily then
if Length(names)>127 then
lesy:=IsWLetterWordsFamily;
else
lesy:=IsBLetterWordsFamily;
fi;
elif lesy=IsBLetterWordsFamily and Length(names)>127 then
lesy:=IsWLetterWordsFamily;
fi;
processed := FreeXArgumentProcessor( "FreeGroup", "f", arg, true, true );
rank := Length( processed.names );

# Construct the family of element objects of our group.
F:= NewFamily( "FreeGroupElementsFamily", IsAssocWordWithInverse
and IsElementOfFreeGroup,
CanEasilySortElements, # the free group can.
CanEasilySortElements # the free group can.
and lesy);
F := NewFamily( "FreeGroupElementsFamily",
IsAssocWordWithInverse and IsElementOfFreeGroup,
CanEasilySortElements,
CanEasilySortElements and processed.lesy );

# Install the data (names, no. of bits available for exponents, types).
StoreInfoFreeMagma( F, names, IsAssocWordWithInverse
and IsElementOfFreeGroup );
StoreInfoFreeMagma( F, processed.names, IsAssocWordWithInverse and
IsElementOfFreeGroup );

# Make the group.
if IsEmpty( names ) then
if rank = 0 then
G:= GroupByGenerators( [], One( F ) );
elif IsFinite( names ) then
G:= GroupByGenerators( List( [ 1 .. Length( names ) ],
elif rank < infinity then
G:= GroupByGenerators( List( [ 1 .. rank ],
i -> ObjByExtRep( F, 1, 1, [ i, 1 ] ) ) );
else
G:= GroupByGenerators( InfiniteListOfGenerators( F ) );
SetIsFinitelyGeneratedGroup( G, false );
fi;

SetIsWholeFamily( G, true );

# Store whether the group is trivial / abelian / solvable
SetIsTrivial( G, Length( names ) = 0 );
SetIsAbelian( G, Length( names ) <= 1 );
SetIsSolvableGroup( G, Length( names ) <= 1 );
# Store whether group is finitely generated / trivial / abelian / solvable.
SetIsFinitelyGeneratedGroup( G, rank < infinity );
SetIsTrivial( G, rank = 0 );
SetIsAbelian( G, rank <= 1 );
SetIsSolvableGroup( G, rank <= 1 );

# Store the whole group in the family.
FamilyObj(G)!.wholeGroup := G;
F!.freeGroup:=G;
SetFilterObj(G,IsGroupOfFamily);

# Return the free group.
return G;
end );

Expand Down
Loading

0 comments on commit 497bc4f

Please sign in to comment.