Skip to content

Commit

Permalink
Introduce FreeXArgumentProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
wilfwilson committed Jun 15, 2021
1 parent b77fb6e commit b7e2c29
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
5 changes: 5 additions & 0 deletions lib/magma.gd
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,11 @@ InParentFOA( "Centralizer", IsMagma, IsObject, DeclareAttribute );
DeclareOperation( "SquareRoots", [ IsMagma, IsMultiplicativeElement ] );


################################################################################
##
DeclareGlobalFunction("FreeXArgumentProcessor");


#############################################################################
##
#F FreeMagma( <rank>[, <name>] )
Expand Down
193 changes: 193 additions & 0 deletions lib/mgmfree.gi
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,199 @@ InstallMethod( MagmaGeneratorsOfFamily,
F -> List( [ 1 .. Length( F!.names ) ], i -> ObjByExtRep( F, i ) ) );


#############################################################################
## Currently used by:
## FreeGroup, FreeMonoid, FreeSemigroup, FreeMagmaWithOne, FreeMagma
InstallGlobalFunction( FreeXArgumentProcessor,
function(
func, # The name of the calling function
name, # The default prefix to use for generator names
arg, # The list of args passed to <func>; to be processed
optional_first_arg, # Whether <func> allows filter as optional 1st arg
allow_rank_zero # Whether <func> support rank-zero objects
)
local wfilt, # a filter for letter or syllable words family
err, # string; helpful error message
form, # string: surmised argument form of the call to <func>
names, # list of generators names
rank, # Length( names )
opt,
init,
word,
x, y1, y2;

# Set up defaults.
wfilt := IsLetterWordsFamily;
err := "";
form := fail;
names := fail;

# Legacy documented feature to give filter for words family with an option.
if func = "FreeGroup" and ValueOption( "FreeGroupFamilyType" ) = "syllable" then
wfilt := IsSyllableWordsFamily; # optional -- used in PQ.
fi;

# The usual way is to give filter in the optional first argument.
if not IsEmpty( arg ) and IsFilter( arg[1] ) then
if not optional_first_arg then
ErrorNoReturn( "the first argument must not be a filter" );
fi;
wfilt := arg[1];
Remove( arg, 1 );
fi;

# Process and validate the argument list, constructing names as necessary.

if Length( arg ) = 0 or arg[1] = 0
or ( Length( arg ) = 1 and IsList( arg[1] ) and IsEmpty( arg[1] ) ) then

# We recognise this function call as a request for a zero-generator object.
if allow_rank_zero then
names := [];
else
Info( InfoWarning, 1, func, " cannot make an object with no generators" );
fi;

# Validate call of form: func( <rank>[, <name> ] ).
elif Length( arg ) <= 2 and IsPosInt( arg[1] ) then

rank := arg[1];

# Documented feature which allows generators to be given custom names in
# objects created by constructors like DihedralGroup or AbelianGroup.
opt := ValueOption( "generatorNames" ); # Note, may be overwritten by arg[2].
if Length( arg ) = 1 and opt <> fail then
if IsString( opt ) then
names := List( [ 1 .. rank ], i -> Concatenation( opt, String( i ) ) );
MakeImmutable( names );
elif ( IsList and IsFinite )( opt ) and rank <= Length( opt )
and ForAll( [ 1 .. rank ],
s -> IsString( opt[s] ) and not IsEmpty( opt[s] ) ) then
names := MakeImmutable( opt{[ 1 .. rank ]} );
else
ErrorNoReturn( Concatenation(
"Cannot process the `generatorNames` option: ",
"the value must be either a single string, or a list ",
"of sufficiently many nonempty strings ",
"(at least ", String( rank ), ", in this case)" ) );
fi;

else
if Length( arg ) = 2 then
name := arg[2];
fi;
if not IsString( name ) then
form := "<rank>, <name>";
err := "<name> must be a string";
else
names := List( [ 1 .. rank ], i -> Concatenation( name, String( i ) ) );
MakeImmutable( names );
fi;
fi;

# Validate call of form: func( <name1>[, <name2>, ...] ), or a list of such.
elif ForAll( arg, IsString ) or Length( arg ) = 1 and IsList( arg[1] ) then

if Length( arg ) = 1 and not IsString( arg[1] ) then
form := "[<name1>, <name2>, ...]";
names := arg[1];
else
form := "<name1>, <name2>, ...";
names := arg;
fi;

# Error checking
if not IsFinite( names ) then
err := "there must be only finitely many names";
elif not ForAll( names, s -> IsString( s ) and not IsEmpty( s ) ) then
err := "the names must be nonempty strings";
fi;

# Validate call of form: func( infinity[, <name>][, <init>] ).
elif Length( arg ) <= 3 and arg[1] = infinity then

init := [];
if Length( arg ) = 3 then
form := "infinity, <name>, <init>";
name := arg[2];
init := arg[3];
elif Length( arg ) = 2 then
if IsList( arg[2] ) and IsFinite( arg[2] ) and not IsEmpty( arg[2] )
and ForAll( arg[2], IsString ) then
form := "infinity, <init>";
init := arg[2];
else
form := "infinity, <name>";
name := arg[2];
fi;
fi;

# Error checking
if not IsString( name ) then
err := "<name> must be a string";
elif not ( IsList( init ) and IsFinite( init ) ) then
err := "<init> must be a finite list";
elif not ForAll( init, s -> IsString( s ) and not IsEmpty( s ) ) then
err := "<init> must consist of nonempty strings";
fi;
if IsEmpty( err ) then
names := InfiniteListOfNames( name, init );
fi;
fi;

# Call to <func> was recognised as having a particular form, but was invalid.
if not IsEmpty( err ) then
ErrorNoReturn( StringFormatted( "{}( {} ): {}", func, form, err ) );
fi;

# Unrecognised call to <func>.
if names = fail then

# Adapt the error message slightly depending on whether the optional
# first argument is supported, and whether at least one argument must be
# given (i.e. whether objects of rank zero are supported).
x := ""; y1 := ""; y2 := "";
if optional_first_arg then
x := "[<wfilt>, ]";
fi;
if allow_rank_zero then
y1 := "["; y2 := "]";
fi;

ErrorNoReturn( StringFormatted( Concatenation(
#"Error,
"usage: {}( {}<rank>[, <name>] )\n",
" {}( {}{}<name1>[, <name2>[, ...]]{} )\n",
" {}( {}<names> )\n",
" {}( {}infinity[, <name>][, <init>] )"
), func, x, func, x, y1, y2, func, x, func, x ) );
fi;

# Process words family options now that we know how many generators there are.
if optional_first_arg then
if not wfilt in [ IsSyllableWordsFamily, IsLetterWordsFamily,
IsWLetterWordsFamily, IsBLetterWordsFamily ] then
ErrorNoReturn( Concatenation(
"the optional first argument <wfilt> must be one of ",
"IsSyllableWordsFamily, IsLetterWordsFamily, IsWLetterWordsFamily, ",
"and IsBLetterWordsFamily" ) );
fi;
if wfilt <> IsSyllableWordsFamily then
if Length( names ) > 127 then
wfilt := IsWLetterWordsFamily;
elif wfilt = IsLetterWordsFamily then
wfilt := IsBLetterWordsFamily;
fi;
fi;
fi;

return rec(
names := names,
lesy := wfilt,
);
end );


#############################################################################
##
#F FreeMagma( <rank> )
Expand Down

0 comments on commit b7e2c29

Please sign in to comment.