diff --git a/gap/quasigroups.gd b/gap/quasigroups.gd index e874d7b..0c9a1be 100644 --- a/gap/quasigroups.gd +++ b/gap/quasigroups.gd @@ -44,14 +44,14 @@ InstallTrueMethod(IsLeftQuotientElement, IsMultiplicativeElementWithInverse); ## DeclareOperation("LeftQuotient", [IsLeftQuotientElement,IsExtLElement]); ## element of a quasigroup -DeclareCategory( "IsQuasigroupElement", - IsMultiplicativeElement and - IsLeftQuotientElement and IsRightQuotientElement ); +DeclareSynonym( "IsQuasigroupElement", + IsMultiplicativeElement and + IsLeftQuotientElement and IsRightQuotientElement ); DeclareRepresentation( "IsQuasigroupElmRep", IsPositionalObjectRep and IsMultiplicativeElement, [1] ); ## element of a loop -DeclareCategory( "IsLoopElement", +DeclareSynonym( "IsLoopElement", IsQuasigroupElement and IsMultiplicativeElementWithInverse ); DeclareRepresentation( "IsLoopElmRep", IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] ); @@ -64,17 +64,11 @@ DeclareCategory("IsRightQuasigroup", DeclareCategory("IsLeftQuasigroup", IsMagma and IsLeftQuotientElementCollection); - -## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup) -DeclareSynonym( "IsLatin", - IsRightQuotientElementCollection and - IsLeftQuotientElementCollection ); - ## quasigroup -DeclareCategory( "IsQuasigroup", IsMagma and IsLatin ); +DeclareSynonym( "IsQuasigroup", IsRightQuasigroup and IsLeftQuasigroup ); ## loop -DeclareCategory( "IsLoop", IsQuasigroup and IsMagmaWithOne and +DeclareSynonym( "IsLoop", IsQuasigroup and IsMagmaWithOne and IsMultiplicativeElementWithInverseCollection); ############################################################################# @@ -139,6 +133,7 @@ DeclareOperation( "IntoGroup", [ IsMagma ] ); ## PRODUCTS OF QUASIGROUPS AND LOOPS ## -------------------------------------------------------------------------- +DeclareGlobalFuntion("ProductTableOfCanonicalCayleyTables"); #DirectProduct already declared for groups. ############################################################################# diff --git a/gap/quasigroups.gi b/gap/quasigroups.gi index 8b612e6..5a65b59 100644 --- a/gap/quasigroups.gi +++ b/gap/quasigroups.gi @@ -732,98 +732,104 @@ end); # groups in GAP. The idea is as follows: # We want to calculate direct product of quasigroups, loops and groups. # If only groups are on the list, standard GAP DirectProduct will take care -# of it. If there are also some quasigroups or loops on the list, -# we must take care of it. +# of it. If there are also some quasigroups or loops on the list (but nothing +# that is not a quasigroup), we must take care of it. # However, we do not know if such a list will be processed with # DirectProductOp( , ), or # DirectProductOp( , ), # since this depends on which algebra is listed first. -# We therefore take care of both situations. +# Call the item in the second argument of DirectProductOp the "distinguished" +# item. To produce the correct result whichever of the two above cases for +# DirectProductOp ends up being called, we add a method in the first case +# which repeats the product call with the first non-group it encounters (if +# any) as the distinguished item. Further, the method for the +# latter case must itself repeat the call with a similar reordering if it +# finds anyitem that is not a quasigroup. -InstallOtherMethod( DirectProductOp, "for DirectProduct( , )", +InstallMethod( DirectProductOp, "for DirectProduct( , )", [ IsList, IsGroup], function( list, first ) local L, p; # Check the arguments. - if IsEmpty( list ) then Error( "LOOPS: <1> must be nonempty." ); fi; - if not ForAny( list, IsQuasigroup ) then - # there are no quasigroups or loops on the list - TryNextMethod(); + if IsEmpty( list ) then + Error( "LOOPS: <1> must be nonempty." ); + elif Length(list) = 1 then + return list[1]; fi; - if ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then - # there are other objects beside groups, loops and quasigroups on the list - TryNextMethod(); - fi; - - # all arguments are groups, quasigroups or loops, and there is at least one loop - # making sure that a loop is listed first so that this method is not called again - for L in list do - if not IsGroup( L ) then - p := Position( list, L ); - list[ 1 ] := L; - list[ p ] := first; - break; + for p in [1..Length(list)] do + if not IsGroup(list[p]) then + return DirectProductOp(Permuted(list, (1,p)), list[p]); fi; od; + # OK, everything is a group, so let the rest of GAP do the work. + TryNextMethod(); +end); - return DirectProductOp( list, list[ 1 ] ); +InstallGlobalFunction(ProductTableOfCanonicalCayleyTables, +function(tablist) + local n, i, nL, nM, TL, TM, T, j, k, s; + TL := tablist[1]; + for s in [2..n] do + TM := tablist[ s ]; + nL := Length( TL); nM := Length( TM ); + T := List( [1..nL*nM], j->[] ); + # not efficient, but it does the job + for i in [1..nM] do for j in [1..nM] do for k in [1..nL] do + Append( T[ (i-1)*nL + k ], TL[ k ] + nL*(TM[i][j]-1) ); + od; od; od; + TL := T; + od; + return TL; end); InstallOtherMethod( DirectProductOp, "for DirectProduct( , )", [ IsList, IsQuasigroup ], function( list, dummy ) - local group_list, quasigroup_list, group_product, are_all_loops, - n, i, nL, nM, TL, TM, T, j, k, s; + local group_list, quasigroup_list, group_product, are_all_loops, n, i, T; # check the arguments if IsEmpty( list ) then Error( "LOOPS: <1> must be nonempty." ); - elif ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then - TryNextMethod(); + elif Length(list) = 1 then + return list(1); fi; + group_list := []; + quasigroup_list := list{[1]}; + are_all_loops := true; + for i in [2..Length(list)] do + if IsGroup(list[i]) then + Add(group_list, list[i]); + elif IsQuasigroup(list[i]) then + Add(quasigroup_list, list[i]); + if are_all_loops and not IsLoop(list[i]) then + are_all_loops := false; + fi; + else # Oops, something that's neither a group or quasigroup + return DirectProductOp(Permuted(list,(1.i)), list[i]); + fi; + od; - # only groups, quasigroups and loops are on the list, with at least one non-group - group_list := Filtered( list, G -> IsGroup( G ) ); - quasigroup_list := Filtered( list, G -> IsQuasigroup( G ) ); + # only groups, quasigroups and loops are on the list, with at least one + # non-group; moreover, we have partitioned the list into groups and + # non-groups and checked whether all quasigroups are really loops. if not IsEmpty( group_list ) then # some groups are on the list group_product := DirectProductOp( group_list, group_list[ 1 ] ); Add( quasigroup_list, IntoLoop( group_product ) ); fi; - # keeping track of whether all algebras are in fact loops - are_all_loops := ForAll( quasigroup_list, IsLoop ); - # now only quasigroups and loops are on the list + # now only quasigroups and loops are on the list, with at least 2 of them n := Length( quasigroup_list ); - if n=1 then - return quasigroup_list[ 1 ]; - fi; - # at least 2 quasigroups and loops; we will not use recursion - # making all Cayley tables canonical - for s in [1..n] do - quasigroup_list[ s ] := - CanonicalCayleyTableOfLeftQuasigroupTable( - CayleyTable( quasigroup_list[ s ] ) ); - od; - # At this point the entries in quasigroup_list are really just tables - for s in [2..n] do - nL := Length( quasigroup_list[ 1 ] ); - nM := Length( quasigroup_list[ s ] ); - TL := quasigroup_list[ 1 ]; - TM := quasigroup_list[ s ]; - T := List( [1..nL*nM], j->[] ); - - # not efficient, but it does the job - for i in [1..nM] do for j in [1..nM] do for k in [1..nL] do - Append( T[ (i-1)*nL + k ], TL[ k ] + nL*(TM[i][j]-1) ); - od; od; od; - quasigroup_list[ 1 ] := T; - od; + # We willl not use recursion; start by making all Cayley tables canonical + Apply(quasigroup_list, + Q -> CanonicalCayleyTableOfLeftQuasigroupTable( CayleyTable( Q ) ) ); + T := ProductTableOfCanonicalCayleyTables( quasigroup_list ); + if are_all_loops then - return LoopByCayleyTable( quasigroup_list[1] ); + return LoopByCayleyTable( T ); fi; - return QuasigroupByCayleyTable( quasigroup_list[1] ); + return QuasigroupByCayleyTable( T ); end ); #############################################################################