diff --git a/lib/semigrp.gd b/lib/semigrp.gd index 38eb26a2994..9cf4c9df753 100644 --- a/lib/semigrp.gd +++ b/lib/semigrp.gd @@ -277,11 +277,10 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ############################################################################# ## -#F FreeSemigroup( [,] ) -#F FreeSemigroup( [,], ) -#F FreeSemigroup( [,], , ... ) -#F FreeSemigroup( [,] ) -#F FreeSemigroup( [,]infinity, , ) +#F FreeSemigroup( [, ][, ] ) +#F FreeSemigroup( [, ][, , ...] ) +#F FreeSemigroup( [, ] ) +#F FreeSemigroup( [, ]infinity[, [, ]] ) ## ## <#GAPDoc Label="FreeSemigroup"> ## @@ -292,17 +291,18 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ## Label="for various names"/> ## -## ## ## ## Called with a positive integer rank, ## returns ## a free semigroup on rank generators. -## If the optional argument name is given then the generators are +## If the optional argument name (a string) is given, +## then the generators are ## printed as name1, name2 etc., ## that is, each name is the concatenation of the string name and an -## integer from 1 to range. +## integer from 1 to rank. ## The default for name is the string "s". ##

## Called in the second form, diff --git a/lib/smgrpfre.gi b/lib/smgrpfre.gi index 05de0ea24a2..67369260d7a 100644 --- a/lib/smgrpfre.gi +++ b/lib/smgrpfre.gi @@ -377,50 +377,113 @@ InstallMethod( GeneratorsSmallest, ############################################################################# ## -#F FreeSemigroup( ) -#F FreeSemigroup( , ) -#F FreeSemigroup( , , ... ) -#F FreeSemigroup( ) -#F FreeSemigroup( infinity, , ) +#F FreeSemigroup( [, ][, ] ) +#F FreeSemigroup( [, ][, , ...] ) +#F FreeSemigroup( [, ] ) +#F FreeSemigroup( [, ]infinity[, [, ]] ) ## InstallGlobalFunction( FreeSemigroup, function( arg ) local names, # list of generators names F, # family of free semigroup element objects zarg, lesy, # filter for letter or syllable words family - S; # free semigroup, result + S, # free semigroup, result + err, # string; helpful error message + form, # string: assumed argument form of call to FreeSemigroup + rank, + name, + init; lesy:=IsLetterWordsFamily; # default: - if IsFilter(arg[1]) then + if not IsEmpty(arg) and 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( "s" ); - 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( "s", 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) ) ); - MakeImmutable( names ); - elif 1 <= Length( zarg ) and ForAll( zarg, IsString ) then - names:= zarg; - elif Length( zarg ) = 1 and IsList( zarg[1] ) - and not IsEmpty( zarg[1] ) - and ForAll( zarg[1], IsString ) then - names:= zarg[1]; + # Process and validate the argument list, constructing names if necessary + err := ""; + + if Length( zarg ) = 0 or zarg[1] = 0 + or (Length(zarg) = 1 and IsList( zarg[1] ) and IsEmpty( zarg[1] )) then + Error("free semigroups of rank zero are not supported"); + + # FreeSemigroup( [, ] ) + elif IsPosInt( zarg[1] ) and Length( zarg ) <= 2 then + + # Get default and optional arguments + rank := zarg[1]; + if Length( zarg ) = 1 then + name := "s"; + else + name := zarg[2]; + fi; + + # Error checking + if not IsString( name ) then + form := ", "; + err := " must be a string"; + + # Construct names + else + names:= List( [ 1 .. rank ], i -> Concatenation( name, String(i) ) ); + MakeImmutable( names ); + fi; + + # FreeSemigroup( [, , ...] ), or a list of such arguments + elif ForAll( zarg, IsString ) or Length( zarg ) = 1 and IsList( zarg[1] ) then + if Length( zarg ) = 1 and not IsString( zarg[1] ) then + form := "[ , , ... ]"; + names:= zarg[1]; + else + form := ", , ..."; + names:= zarg; + fi; + if not ForAll( names, s -> IsString(s) and not IsEmpty(s) ) then + err := "the names must be nonempty strings"; + fi; + + # FreeSemigroup( infinity[, [, ]] ) + elif zarg[1] = infinity and Length( zarg ) <= 3 then + + # Get default and optional arguments + name := "s"; + init := []; + if Length( zarg ) = 3 then + form := "infinity, , "; + name := zarg[2]; + init := zarg[3]; + elif Length( zarg ) = 2 then + form := "infinity, "; + name := zarg[2]; + fi; + + # Error checking + if not IsString( name ) then + err := " must be a string"; + fi; + if not ( IsList( init ) and ForAll( init, s -> IsString(s) and not IsEmpty(s) ) ) then + if not IsEmpty(err) then + Append(err, " and "); + fi; + Append(err, " must be a list of nonempty strings"); + fi; + + # Construct names + if IsEmpty(err) then + names:= InfiniteListOfNames( name, init ); + fi; + else - Error("usage: FreeSemigroup(,..),FreeSemigroup()"); + ErrorNoReturn("""usage: FreeSemigroup( [, ][, ] ) + FreeSemigroup( [, ][, , ...] ) + FreeSemigroup( [, ] ) + FreeSemigroup( [, ]infinity[, [, ]] )"""); + fi; + + if not IsEmpty(err) then + ErrorNoReturn(StringFormatted("FreeSemigroup( {} ): {}", form, err)); fi; # deal with letter words family types @@ -457,6 +520,7 @@ InstallGlobalFunction( FreeSemigroup, function( arg ) SetIsFreeSemigroup(S,true); SetIsWholeFamily( S, true ); SetIsTrivial( S, false ); + SetIsCommutative( S, Length(names) = 1 ); return S; end ); diff --git a/tst/testbugfix/2021-04-08-empty-FreeSemigroup.tst b/tst/testbugfix/2021-04-08-empty-FreeSemigroup.tst new file mode 100644 index 00000000000..50822d1839a --- /dev/null +++ b/tst/testbugfix/2021-04-08-empty-FreeSemigroup.tst @@ -0,0 +1,11 @@ +# See https://github.com/gap-system/gap/issues/1385 +gap> FreeSemigroup(); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup([]); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(""); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(0); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(0, "name"); +Error, free semigroups of rank zero are not supported diff --git a/tst/testinstall/smgrpfre.tst b/tst/testinstall/smgrpfre.tst new file mode 100644 index 00000000000..668e2eaf97e --- /dev/null +++ b/tst/testinstall/smgrpfre.tst @@ -0,0 +1,127 @@ +#@local F +gap> START_TEST("smgrpfre.tst"); + +# FreeSemigroup +gap> FreeSemigroup(fail); +Error, usage: FreeSemigroup( [, ][, ] ) + FreeSemigroup( [, ][, , ...] ) + FreeSemigroup( [, ] ) + FreeSemigroup( [, ]infinity[, [, ]] ) + +# FreeSemigroup: rank 0 +gap> FreeSemigroup(); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup([]); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(""); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(0); +Error, free semigroups of rank zero are not supported +gap> FreeSemigroup(0, "name"); +Error, free semigroups of rank zero are not supported + +# FreeSemigroup(infinity[, name[, init]]) +gap> FreeSemigroup(infinity); + +gap> FreeSemigroup(infinity, fail); +Error, FreeSemigroup( infinity, ): must be a string +gap> FreeSemigroup(infinity, []); + +gap> FreeSemigroup(infinity, ""); + +gap> FreeSemigroup(infinity, "nicename"); + +gap> FreeSemigroup(infinity, fail, fail); +Error, FreeSemigroup( infinity, , ): must be a string and \ + must be a list of nonempty strings +gap> FreeSemigroup(infinity, "nicename", fail); +Error, FreeSemigroup( infinity, , ): must be a list of non\ +empty strings +gap> FreeSemigroup(infinity, "gen", []); + +gap> FreeSemigroup(infinity, "gen", [""]); +Error, FreeSemigroup( infinity, , ): must be a list of non\ +empty strings +gap> FreeSemigroup(infinity, "gen", ["starter"]); + +gap> FreeSemigroup(infinity, "gen", ["starter", ""]); +Error, FreeSemigroup( infinity, , ): must be a list of non\ +empty strings +gap> F := FreeSemigroup(infinity, "gen", ["starter", "second", "third"]); + +gap> GeneratorsOfSemigroup(F){[1 .. 4]}; +[ starter, second, third, gen4 ] +gap> FreeSemigroup(infinity, "gen", ["starter"], fail); +Error, usage: FreeSemigroup( [, ][, ] ) + FreeSemigroup( [, ][, , ...] ) + FreeSemigroup( [, ] ) + FreeSemigroup( [, ]infinity[, [, ]] ) + +# FreeSemigroup(rank[, name]) +gap> F := FreeSemigroup(1); + +gap> HasIsCommutative(F) and IsCommutative(F); +true +gap> F := FreeSemigroup(2); + +gap> HasIsCommutative(F) and not IsCommutative(F); +true +gap> F := FreeSemigroup(10); + +gap> F := FreeSemigroup(3, fail); +Error, FreeSemigroup( , ): must be a string +gap> F := FreeSemigroup(4, ""); + +gap> F := FreeSemigroup(5, []); + +gap> F := FreeSemigroup(4, "cheese"); + +gap> FreeSemigroup(3, "car", fail); +Error, usage: FreeSemigroup( [, ][, ] ) + FreeSemigroup( [, ][, , ...] ) + FreeSemigroup( [, ] ) + FreeSemigroup( [, ]infinity[, [, ]] ) + +# FreeSemigroup( [, , ...] ) +gap> FreeSemigroup("", "second"); +Error, FreeSemigroup( , , ... ): the names must be nonempty stri\ +ngs +gap> FreeSemigroup("first", ""); +Error, FreeSemigroup( , , ... ): the names must be nonempty stri\ +ngs +gap> FreeSemigroup("first", []); +Error, FreeSemigroup( , , ... ): the names must be nonempty stri\ +ngs +gap> FreeSemigroup([], []); +Error, FreeSemigroup( , , ... ): the names must be nonempty stri\ +ngs +gap> FreeSemigroup("bacon", "eggs", "beans"); + +gap> FreeSemigroup("shed"); + + +# FreeSemigroup( [ [, , ...] ] ) +gap> FreeSemigroup(["", "second"]); +Error, FreeSemigroup( [ , , ... ] ): the names must be nonempty \ +strings +gap> FreeSemigroup(["first", ""]); +Error, FreeSemigroup( [ , , ... ] ): the names must be nonempty \ +strings +gap> FreeSemigroup(["first", []]); +Error, FreeSemigroup( [ , , ... ] ): the names must be nonempty \ +strings +gap> FreeSemigroup([[], []]); +Error, FreeSemigroup( [ , , ... ] ): the names must be nonempty \ +strings +gap> FreeSemigroup(["bacon", "eggs", "beans"]); + +gap> FreeSemigroup(["grid"]); + +gap> FreeSemigroup(["grid"], fail); +Error, usage: FreeSemigroup( [, ][, ] ) + FreeSemigroup( [, ][, , ...] ) + FreeSemigroup( [, ] ) + FreeSemigroup( [, ]infinity[, [, ]] ) + +# +gap> STOP_TEST( "smgrpfre.tst", 1);