From 04b4fa90bbb6a4dd59af6f109f5b53c2b93afb0d Mon Sep 17 00:00:00 2001 From: Wilf Wilson Date: Fri, 11 Jun 2021 01:40:41 +0100 Subject: [PATCH] Disallow duplicate names gens for magmas, semigroups, groups, etc --- doc/ref/grplib.xml | 2 +- lib/algebra.gd | 12 ++++++++---- lib/algebra.gi | 4 ++++ lib/alglie.gd | 1 + lib/grpfree.gd | 9 +++++---- lib/magma.gd | 18 ++++++++++-------- lib/mgmfree.gi | 22 ++++++++++++++++++++-- lib/monoid.gd | 9 +++++---- lib/semigrp.gd | 8 +++++--- tst/testinstall/grpfree.tst | 4 ++++ tst/testinstall/mgmfree.tst | 8 ++++++++ tst/testinstall/monofree.tst | 4 ++++ tst/testinstall/smgrpfre.tst | 8 ++++---- 13 files changed, 79 insertions(+), 30 deletions(-) diff --git a/doc/ref/grplib.xml b/doc/ref/grplib.xml index cc8061fe59..e3b49e639e 100644 --- a/doc/ref/grplib.xml +++ b/doc/ref/grplib.xml @@ -106,7 +106,7 @@ It is possible to influence this naming with the option generatorNames, see Section . If this option holds a string, then the generators are named with this string and sequential numbers starting with 1. -If this option holds a list of sufficient length consisting of +If this option holds a list of sufficient length consisting of distinct nonempty strings, then the generator names are taken from this list, in order.

## is a free (nonassociative) algebra of rank rank ## over the division ring R. -## Here name, and name1, name2, ... are optional strings +## Here name, and name1, name2, ... are optional +## distinct nonempty strings ## that can be used to provide names for the generators. ## A:= FreeAlgebra( Rationals, "a", "b" ); @@ -1523,7 +1524,8 @@ DeclareGlobalFunction( "FreeAlgebra" ); ## ## is a free (nonassociative) algebra-with-one of rank rank ## over the division ring R. -## Here name, and name1, name2, ... are optional strings +## Here name, and name1, name2, ... are optional +## distinct nonempty strings ## that can be used to provide names for the generators. ## A:= FreeAlgebraWithOne( Rationals, 4, "q" ); @@ -1555,7 +1557,8 @@ DeclareGlobalFunction( "FreeAlgebraWithOne" ); ## ## is a free associative algebra of rank rank over the ## division ring R. -## Here name, and name1, name2, ... are optional strings +## Here name, and name1, name2, ... are optional +## distinct nonempty strings ## that can be used to provide names for the generators. ## A:= FreeAssociativeAlgebra( GF( 5 ), 4, "a" ); @@ -1583,7 +1586,8 @@ DeclareGlobalFunction( "FreeAssociativeAlgebra" ); ## ## is a free associative algebra-with-one of rank rank over the ## division ring R. -## Here name, and name1, name2, ... are optional strings +## Here name, and name1, name2, ... are optional +## distinct nonempty strings ## that can be used to provide names for the generators. ## A:= FreeAssociativeAlgebraWithOne( Rationals, "a", "b", "c" ); diff --git a/lib/algebra.gi b/lib/algebra.gi index cf086ff597..4006198931 100644 --- a/lib/algebra.gi +++ b/lib/algebra.gi @@ -3322,6 +3322,10 @@ BindGlobal( "FreeAlgebraConstructor", function( name, magma ) "or ", name, "( , , ... )" ); fi; + if not IsDuplicateFreeList(names) then + Error( "the given generator names must be distinct" ); + fi; + M := magma( names ); # Construct the algebra as free magma algebra of a free magma over `R'. diff --git a/lib/alglie.gd b/lib/alglie.gd index 31cc718ee0..e2b235d778 100644 --- a/lib/alglie.gd +++ b/lib/alglie.gd @@ -1253,6 +1253,7 @@ DeclareAttribute( ## FreeLieAlgebra( R, name1, name2,...) returns ## a free Lie algebra over R with generators named name1, ## name2, and so on. +## Note that any provided generator names must be distinct. ## The elements of a free Lie algebra are written on the Hall-Lyndon ## basis. ## ## 2: For given generator names ## -## Called with various nonempty strings, +## Called with various distinct nonempty strings, ## returns ## a free group on as many generators as arguments, which are labelled ## name1, name2, etc. ## ## 3: For a given list of generator names ## -## Called with a finite list names of +## Called with a finite duplicate-free list names of ## nonempty strings, ## returns ## a free group on Length(names) generators, whose @@ -104,11 +104,12 @@ DeclareSynonym( "IsElementOfFreeGroupFamily",IsAssocWordWithInverseFamily ); ## The optional argument name must be a string; its default value is ## "f", ## and the optional argument init must be a finite list of -## nonempty strings; its default value is an empty list. +## distinct nonempty strings; its default value is an empty list. ## The generators are initially labelled according to the list init, ## followed by ## namei for each i in the range from -## Length(init)+1 to infinity. +## Length(init)+1 to infinity; such a label is not +## allowed to appear in init. ## ## ## If the optional first argument wfilt is given, then it must be either diff --git a/lib/magma.gd b/lib/magma.gd index 975244d64c..6d706c6647 100644 --- a/lib/magma.gd +++ b/lib/magma.gd @@ -818,7 +818,7 @@ DeclareGlobalFunction("FreeXArgumentProcessor"); ## ## 2: For given generator names ## -## Called with various (at least one) nonempty strings, +## Called with various (one or more) distinct nonempty strings, ## returns ## a free magma on as many generators as arguments, which are labelled ## name1, name2, etc. @@ -826,7 +826,7 @@ DeclareGlobalFunction("FreeXArgumentProcessor"); ## 3: For a given list of generator names ## ## Called with a finite nonempty list names of -## nonempty strings, +## distinct nonempty strings, ## returns ## a free magma on Length(names) generators, whose ## i-th generator is labelled names[i]. @@ -843,11 +843,12 @@ DeclareGlobalFunction("FreeXArgumentProcessor"); ## The optional argument name must be a string; its default value is ## "x", ## and the optional argument init must be a finite list of -## nonempty strings; its default value is an empty list. +## distinct nonempty strings; its default value is an empty list. ## The generators are initially labelled according to the list init, ## followed by ## namei for each i in the range from -## Length(init)+1 to infinity. +## Length(init)+1 to infinity; such a label is not +## allowed to appear in init. ## ## ## ## 2: For given generator names ## -## Called with various nonempty strings, +## Called with various (one or more) distinct nonempty strings, ## returns ## a free magma-with-one on as many generators as arguments, which are ## labelled name1, name2, etc. @@ -939,7 +940,7 @@ DeclareGlobalFunction( "FreeMagma" ); ## 3: For a given list of generator names ## ## Called with a finite list names of -## nonempty strings, +## distinct nonempty strings, ## returns ## a free magma-with-one on Length(names) generators, whose ## i-th generator is labelled names[i]. @@ -956,11 +957,12 @@ DeclareGlobalFunction( "FreeMagma" ); ## The optional argument name must be a string; its default value is ## "x", ## and the optional argument init must be a finite list of -## nonempty strings; its default value is an empty list. +## distinct nonempty strings; its default value is an empty list. ## The generators are initially labelled according to the list init, ## followed by ## namei for each i in the range from -## Length(init)+1 to infinity. +## Length(init)+1 to infinity; such a label is not +## allowed to appear in init. ## ## ## IsString( opt[s] ) and not IsEmpty( opt[s] ) ) then + s -> IsString( opt[s] ) and not IsEmpty( opt[s] ) ) + and IsDuplicateFreeList( opt{[ 1 .. rank ]} ) 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 ", + "of sufficiently many distinct nonempty strings ", "(at least ", String( rank ), ", in this case)" ) ); fi; @@ -378,6 +380,8 @@ function( 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"; + elif not IsDuplicateFreeList( names ) then + err := "the names must be distinct"; fi; # Validate call of form: func( infinity[, ][, ] ). @@ -404,8 +408,22 @@ function( err := " must be a string"; elif not ( IsList( init ) and IsFinite( init ) ) then err := " must be a finite list"; + elif not IsDuplicateFreeList( init ) then + err := " must be duplicate-free"; elif not ForAll( init, s -> IsString( s ) and not IsEmpty( s ) ) then err := " must consist of nonempty strings"; + else + for word in init do + if StartsWith( word, name ) then + x := word{[ Length( name ) + 1 .. Length( word ) ]}; + if not IsEmpty( x ) and ForAll( x, IsDigitChar ) + and Int( x ) > Length( init ) then + err := "no member of may repeat any of the infinitely many "; + Append( err, "generators that begin with " ); + break; + fi; + fi; + od; fi; if IsEmpty( err ) then names := InfiniteListOfNames( name, init ); diff --git a/lib/monoid.gd b/lib/monoid.gd index 223c608195..cddc41b9d7 100644 --- a/lib/monoid.gd +++ b/lib/monoid.gd @@ -231,7 +231,7 @@ DeclareSynonymAttr( "TrivialSubmonoid", TrivialSubmagmaWithOne ); ## ## 2: For given generator names ## -## Called with various nonempty strings, +## Called with various (one or more) distinct nonempty strings, ## returns ## a free monoid on as many generators as arguments, which are labelled ## name1, name2, etc. @@ -239,7 +239,7 @@ DeclareSynonymAttr( "TrivialSubmonoid", TrivialSubmagmaWithOne ); ## 3: For a given list of generator names ## ## Called with a finite list names of -## nonempty strings, +## distinct nonempty strings, ## returns ## a free monoid on Length(names) generators, whose ## i-th generator is labelled names[i]. @@ -256,11 +256,12 @@ DeclareSynonymAttr( "TrivialSubmonoid", TrivialSubmagmaWithOne ); ## The optional argument name must be a string; its default value is ## "m", ## and the optional argument init must be a finite list of -## nonempty strings; its default value is an empty list. +## distinct nonempty strings; its default value is an empty list. ## The generators are initially labelled according to the list init, ## followed by ## namei for each i in the range from -## Length(init)+1 to infinity. +## Length(init)+1 to infinity; such a label is not +## allowed to appear in init. ## ## ## diff --git a/lib/semigrp.gd b/lib/semigrp.gd index 12765fa6ed..70b3b75d9a 100644 --- a/lib/semigrp.gd +++ b/lib/semigrp.gd @@ -300,7 +300,7 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ## 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; +## and do not necessarily distinguish elements of the semigroup; ## see the examples at the end for more information. ## ## @@ -323,7 +323,7 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ## ## 2: For given generator names ## -## Called with various (at least one) nonempty strings, +## Called with various (one or more) distinct nonempty strings, ## returns ## a free semigroup on as many generators as arguments, which are labelled ## name1, name2, etc. @@ -331,7 +331,7 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ## 3: For a given list of generator names ## ## Called with a nonempty finite list names of -## nonempty strings, +## distinct nonempty strings, ## returns ## a free semigroup on Length(names) generators, whose ## i-th generator is labelled names[i]. @@ -417,6 +417,8 @@ DeclareAttribute("CayleyGraphDualSemigroup",IsSemigroup); ## distinguish letters. ## It is possible to create arbitrarily weird situations by choosing strange ## names for the letters. +## However, as a small step to avoiding confusion, it is disallowed to choose +## duplicate generator names. ##

## f := FreeGroup( "x", "x" ); diff --git a/tst/testinstall/grpfree.tst b/tst/testinstall/grpfree.tst index 7271f9c467..ea4c456f26 100644 --- a/tst/testinstall/grpfree.tst +++ b/tst/testinstall/grpfree.tst @@ -167,6 +167,10 @@ gap> FreeGroup("bacon", "eggs", "beans"); gap> FreeGroup("shed"); +gap> FreeGroup("shed", "shed"); +Error, FreeGroup( , , ... ): the names must be distinct +gap> FreeGroup("a", "b", "c", "d", "b", "e", "f"); +Error, FreeGroup( , , ... ): the names must be distinct # FreeGroup( [ , , ... ] ) gap> FreeGroup(InfiniteListOfNames("a")); diff --git a/tst/testinstall/mgmfree.tst b/tst/testinstall/mgmfree.tst index 814c558adc..8d454cc27d 100644 --- a/tst/testinstall/mgmfree.tst +++ b/tst/testinstall/mgmfree.tst @@ -113,6 +113,10 @@ gap> FreeMagma("bacon", "eggs", "beans"); gap> FreeMagma("shed"); +gap> FreeMagma("shed", "shed"); +Error, FreeMagma( , , ... ): the names must be distinct +gap> FreeMagma("a", "b", "c", "d", "b", "e", "f"); +Error, FreeMagma( , , ... ): the names must be distinct # FreeMagma( [ [, , ...] ] ) gap> FreeMagma(InfiniteListOfNames("a")); @@ -238,6 +242,10 @@ gap> FreeMagmaWithOne("bacon", "eggs", "beans"); gap> FreeMagmaWithOne("shed"); +gap> FreeMagmaWithOne("shed", "shed"); +Error, FreeMagmaWithOne( , , ... ): the names must be distinct +gap> FreeMagmaWithOne("a", "b", "c", "d", "b", "e", "f"); +Error, FreeMagmaWithOne( , , ... ): the names must be distinct # FreeMagmaWithOne( [ [, , ...] ] ) gap> FreeMagmaWithOne(InfiniteListOfNames("a")); diff --git a/tst/testinstall/monofree.tst b/tst/testinstall/monofree.tst index 7897a3c588..a99e4e5656 100644 --- a/tst/testinstall/monofree.tst +++ b/tst/testinstall/monofree.tst @@ -133,6 +133,10 @@ gap> FreeMonoid("bacon", "eggs", "beans"); gap> FreeMonoid("shed"); +gap> FreeMonoid("shed", "shed"); +Error, FreeMonoid( , , ... ): the names must be distinct +gap> FreeMonoid("a", "b", "c", "d", "b", "e", "f"); +Error, FreeMonoid( , , ... ): the names must be distinct # FreeMonoid( [ , , ... ] ) gap> FreeMonoid(InfiniteListOfNames("a")); diff --git a/tst/testinstall/smgrpfre.tst b/tst/testinstall/smgrpfre.tst index 4423b21e18..a94495e464 100644 --- a/tst/testinstall/smgrpfre.tst +++ b/tst/testinstall/smgrpfre.tst @@ -147,8 +147,8 @@ Error, usage: FreeSemigroup( [, ][, ] ) # generatorNames gap> FreeSemigroup(5 : generatorNames := false); Error, Cannot process the `generatorNames` option: the value must be either a \ -single string, or a list of sufficiently many nonempty strings (at least 5, in\ - this case) +single string, or a list of sufficiently many distinct nonempty strings (at le\ +ast 5, in this case) gap> PushOptions( rec( generatorNames := fail ) ); gap> FreeSemigroup(3 : generatorNames := ""); @@ -156,8 +156,8 @@ gap> FreeSemigroup(2 : generatorNames := "cool"); gap> FreeSemigroup(2 : generatorNames := ["red"]); Error, Cannot process the `generatorNames` option: the value must be either a \ -single string, or a list of sufficiently many nonempty strings (at least 2, in\ - this case) +single string, or a list of sufficiently many distinct nonempty strings (at le\ +ast 2, in this case) gap> PushOptions( rec( generatorNames := fail ) ); gap> FreeSemigroup(2 : generatorNames := ["red", "yellow"]);