Compare commits

...

37 Commits

Author SHA1 Message Date
Glen Whitney
d7f394f957 Correct the TrueMethods: elt must be associate and have inverses to have quotients 2018-09-12 08:34:44 -04:00
Glen Whitney
1cf79a02c3 small amendments. 2017-10-30 12:30:15 -04:00
Glen Whitney
03d80d9acb Increase priority of LOOPS IsCommutative. 2017-10-30 12:14:36 -04:00
Glen Whitney
69a866d5db Maybe if we undup the declaration. 2017-10-30 12:11:29 -04:00
Glen Whitney
b3b9e3817b Can IsCommutative be a regular method. 2017-10-30 12:09:24 -04:00
Glen Whitney
aaaf1a04a9 typo in that. 2017-10-30 11:59:07 -04:00
Glen Whitney
8f7f6891ec loop element reps are quasigroup element reps. 2017-10-30 11:58:26 -04:00
Glen Whitney
4c2817b04f define opposites even if just have cayley table. 2017-10-30 11:53:35 -04:00
Glen Whitney
2dcde99549 oops need to check first factor for being loop in DirectProductLoop. 2017-10-30 11:45:14 -04:00
Glen Whitney
143e51b801 oops parens where there should have been brackets. 2017-10-30 11:38:00 -04:00
Glen Whitney
4254b760c9 the quasigroup elm rep should be a quasigroup element 2017-10-30 11:32:44 -04:00
Glen Whitney
8c5670ef3f typo in last 2017-10-30 11:24:56 -04:00
Glen Whitney
2696cd6ff2 Have to cut many other methods down to be more specific to element reps 2017-10-30 11:23:55 -04:00
Glen Whitney
a69dcd05d3 Fix a couple of typos 2017-10-30 01:00:42 -04:00
Glen Whitney
244492a7ed Finish the merge with 3.4.0
Aha, somehow I did not include the PackageInfo.g and the properly merged
gap/quasigroups.gd into the last commit; here they are
2017-10-30 00:35:55 -04:00
Glen Whitney
91ba2744c1 Merge branch 'Loops3.4.0'
Incorporate the changes from 3.3.0 to 3.4.0 of LOOPS into this
development. These were mostly straightforward. The only conflicts were
in quasigroups.gd, in which all of the changes from this development
were selected, as "IsLatin" had already been removed.
2017-10-30 00:33:44 -04:00
Glen Whitney
fb39c9e02e Print methods for quasigroup/loop elms jsut for this specific representation 2017-10-29 15:12:01 -04:00
Glen Whitney
71672eeb9c Oops, unused local variable 2017-10-26 21:15:21 -04:00
Glen Whitney
df74177abb typo 2017-10-25 16:28:33 +02:00
Glen Whitney
1493a9c480 factor out parts of direct product computation that can be reused for racks 2017-10-25 16:18:04 +02:00
Glen Whitney
68390c3869 oops lapsed back to python 0-based 2017-10-24 23:12:35 +02:00
Glen Whitney
2521a38635 Generalize CayleyTableByPerms so that it is more useful in rack/quandle context 2017-10-24 16:16:27 +02:00
Glen Whitney
54c503e356 Don't create multiplication tables just for the sake of Opposite 2017-10-20 11:06:49 +02:00
Glen Whitney
6d095fe0d3 Remove stray comment 2017-10-19 03:11:29 +02:00
Glen Whitney
7c3f421a20 delete leftover code 2017-10-19 02:12:38 +02:00
Glen Whitney
1ec279ab0b stupid brackets 2017-10-19 02:11:34 +02:00
Glen Whitney
a4d79ff66e provide facility to bind element names 2017-10-19 02:10:01 +02:00
Glen Whitney
87e7b01333 provide facility to bind element names 2017-10-19 02:08:47 +02:00
Glen Whitney
88533fd7de extend the element naming prefix to more general setting, and so use more elaborate identifier 2017-10-19 01:11:26 +02:00
Glen Whitney
9d8dfdb9e2 Fix more typo. 2017-10-18 22:05:39 +02:00
Glen Whitney
c848f29524 Fix typo. 2017-10-18 22:00:16 +02:00
Glen Whitney
1ac424c524 Lift some functions to magmas in general 2017-10-18 21:36:03 +02:00
Glen Whitney
cf2bc14423 Looks like quasigroup elements have to be multiplicative elements (as that is not implied by ExtL and ExtRElement) 2017-10-17 22:28:41 +02:00
Glen Whitney
66b00d7d34 Close a parenthesis. 2017-10-17 21:59:04 +02:00
Glen Whitney
75fe6fa0e3 Fix typo. 2017-10-17 21:57:31 +02:00
Glen Whitney
e05b2a6deb Properties take just a single filter, I guess. 2017-10-17 21:56:00 +02:00
Glen Whitney
722f25e51f Rudimentary support for left/right quasigroups. 2017-10-17 21:42:47 +02:00
6 changed files with 438 additions and 260 deletions

View File

@ -1,8 +1,8 @@
SetPackageInfo( rec( SetPackageInfo( rec(
PackageName := "loops", PackageName := "loops",
Subtitle := "Computing with quasigroups and loops in GAP", Subtitle := "Computing with quasigroups and loops in GAP",
Version := "3.4.0", Version := "3.4.1",
Date := "27/10/2017", Date := "29/10/2017",
ArchiveURL := "http://www.math.du.edu/loops/loops-3.4.0", ArchiveURL := "http://www.math.du.edu/loops/loops-3.4.0",
ArchiveFormats := "-win.zip .tar.gz", ArchiveFormats := "-win.zip .tar.gz",

View File

@ -13,7 +13,7 @@
## ------------------------------------------------------------------------- ## -------------------------------------------------------------------------
DeclareProperty( "IsAssociative", IsLoop ); DeclareProperty( "IsAssociative", IsLoop );
DeclareProperty( "IsCommutative", IsQuasigroup ); #DeclareProperty( "IsCommutative", IsQuasigroup ); # Already covered by GAP
DeclareProperty( "IsPowerAssociative", IsQuasigroup ); DeclareProperty( "IsPowerAssociative", IsQuasigroup );
DeclareProperty( "IsDiassociative", IsQuasigroup ); DeclareProperty( "IsDiassociative", IsQuasigroup );

View File

@ -38,8 +38,8 @@ InstallTrueMethod( IsExtraLoop, IsAssociative and IsLoop );
## ##
## Returns true if <Q> is commutative. ## Returns true if <Q> is commutative.
InstallOtherMethod( IsCommutative, "for quasigroup", InstallMethod( IsCommutative, "for quasigroup",
[ IsQuasigroup ], [ IsQuasigroup ], 20, # Need to beat GAP's library methods
function( Q ) function( Q )
return LeftSection( Q ) = RightSection( Q ); return LeftSection( Q ) = RightSection( Q );
end ); end );

View File

@ -12,34 +12,34 @@
## DISPLAYING AND COMPARING ELEMENTS ## DISPLAYING AND COMPARING ELEMENTS
## ------------------------------------------------------------------------- ## -------------------------------------------------------------------------
InstallMethod( PrintObj, "for a quasigroup element", InstallMethod( PrintObj, "for a default quasigroup element",
[ IsQuasigroupElement ], [ IsQuasigroupElmRep ],
function( obj ) function( obj )
local F; local F;
F := FamilyObj( obj ); F := FamilyObj( obj );
Print( F!.names, obj![ 1 ] ); Print( F!.elmNamePrefix, obj![ 1 ] );
end ); end );
InstallMethod( PrintObj, "for a loop element", InstallMethod( PrintObj, "for a loop element",
[ IsLoopElement ], [ IsLoopElmRep ],
function( obj ) function( obj )
local F; local F;
F := FamilyObj( obj ); F := FamilyObj( obj );
Print( F!.names, obj![ 1 ] ); Print( F!.elmNamePrefix, obj![ 1 ] );
end ); end );
InstallMethod( \=, "for two elements of a quasigroup", InstallMethod( \=, "for two elements of a quasigroup",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] = y![ 1 ]; return x![ 1 ] = y![ 1 ];
end ); end );
InstallMethod( \<, "for two elements of a quasigroup", InstallMethod( \<, "for two elements of a quasigroup",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] < y![ 1 ]; return x![ 1 ] < y![ 1 ];
end ); end );
InstallMethod( \., "for quasigroup and positive integer", InstallMethod( \., "for quasigroup and positive integer",
@ -57,7 +57,7 @@ end );
## i.e., a*b*c=(a*b)*c. Powers use binary decomposition. ## i.e., a*b*c=(a*b)*c. Powers use binary decomposition.
InstallMethod( \*, "for two quasigroup elements", InstallMethod( \*, "for two quasigroup elements",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
local F; local F;
F := FamilyObj( x ); F := FamilyObj( x );
@ -65,13 +65,13 @@ function( x, y )
end ); end );
InstallOtherMethod( \*, "for a QuasigroupElement and a list", InstallOtherMethod( \*, "for a QuasigroupElement and a list",
[ IsQuasigroupElement , IsList ], [ IsQuasigroupElmRep , IsList ],
function( x, ly ) function( x, ly )
return List( ly, y -> x*y ); return List( ly, y -> x*y );
end ); end );
InstallOtherMethod( \*, "for a list and a QuasigroupElement", InstallOtherMethod( \*, "for a list and a QuasigroupElement",
[ IsList, IsQuasigroupElement ], [ IsList, IsQuasigroupElmRep ],
function( lx, y ) function( lx, y )
return List( lx, x -> x*y ); return List( lx, x -> x*y );
end ); end );
@ -83,7 +83,7 @@ end );
## z=x/y means zy=x ## z=x/y means zy=x
InstallMethod( RightDivision, "for two quasigroup elements", InstallMethod( RightDivision, "for two quasigroup elements",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
local F, ycol; local F, ycol;
F := FamilyObj( x ); F := FamilyObj( x );
@ -93,7 +93,7 @@ end );
InstallOtherMethod( RightDivision, InstallOtherMethod( RightDivision,
"for a list and a quasigroup element", "for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ], [ IsList, IsQuasigroupElmRep ],
0, 0,
function( lx, y ) function( lx, y )
return List( lx, x -> RightDivision(x, y) ); return List( lx, x -> RightDivision(x, y) );
@ -101,7 +101,7 @@ end );
InstallOtherMethod( RightDivision, InstallOtherMethod( RightDivision,
"for a quasigroup element and a list", "for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ], [ IsQuasigroupElmRep, IsList ],
0, 0,
function( x, ly ) function( x, ly )
return List( ly, y -> RightDivision(x, y) ); return List( ly, y -> RightDivision(x, y) );
@ -110,7 +110,7 @@ end );
InstallOtherMethod( \/, InstallOtherMethod( \/,
"for two elements of a quasigroup", "for two elements of a quasigroup",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
0, 0,
function( x, y ) function( x, y )
return RightDivision( x, y ); return RightDivision( x, y );
@ -118,7 +118,7 @@ end );
InstallOtherMethod( \/, InstallOtherMethod( \/,
"for a list and a quasigroup element", "for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ], [ IsList, IsQuasigroupElmRep ],
0, 0,
function( lx, y ) function( lx, y )
return List( lx, x -> RightDivision(x, y) ); return List( lx, x -> RightDivision(x, y) );
@ -126,7 +126,7 @@ end );
InstallOtherMethod( \/, InstallOtherMethod( \/,
"for a quasigroup element and a list", "for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ], [ IsQuasigroupElmRep, IsList ],
0, 0,
function( x, ly ) function( x, ly )
return List( ly, y -> RightDivision(x, y) ); return List( ly, y -> RightDivision(x, y) );
@ -135,7 +135,7 @@ end );
## z = x\y means xz=y ## z = x\y means xz=y
InstallMethod( LeftDivision, "for two quasigroup elements", InstallMethod( LeftDivision, "for two quasigroup elements",
IsIdenticalObj, IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
local F; local F;
F := FamilyObj( x ); F := FamilyObj( x );
@ -144,7 +144,7 @@ end );
InstallOtherMethod( LeftDivision, InstallOtherMethod( LeftDivision,
"for a list and a quasigroup element", "for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ], [ IsList, IsQuasigroupElmRep ],
0, 0,
function( lx, y ) function( lx, y )
return List( lx, x -> LeftDivision(x, y) ); return List( lx, x -> LeftDivision(x, y) );
@ -152,7 +152,7 @@ end );
InstallOtherMethod( LeftDivision, InstallOtherMethod( LeftDivision,
"for a quasigroup element and a list", "for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ], [ IsQuasigroupElmRep, IsList ],
0, 0,
function( x, ly ) function( x, ly )
return List( ly, y -> LeftDivision(x, y) ); return List( ly, y -> LeftDivision(x, y) );
@ -215,7 +215,7 @@ end );
## ------------------------------------------------------------------------- ## -------------------------------------------------------------------------
InstallMethod( \^, "for a quasigroup element and a permutation", InstallMethod( \^, "for a quasigroup element and a permutation",
[ IsQuasigroupElement, IsPerm ], [ IsQuasigroupElmRep, IsPerm ],
function( x, p ) function( x, p )
local F; local F;
F := FamilyObj( x ); F := FamilyObj( x );
@ -223,7 +223,7 @@ function( x, p )
end ); end );
InstallMethod( OneOp, "for loop elements", InstallMethod( OneOp, "for loop elements",
[ IsLoopElement ], [ IsLoopElmRep ],
function( x ) function( x )
local F; local F;
F := FamilyObj( x ); F := FamilyObj( x );
@ -237,7 +237,7 @@ end );
## If <x> is a loop element, returns the left inverse of <x> ## If <x> is a loop element, returns the left inverse of <x>
InstallMethod( LeftInverse, "for loop elements", InstallMethod( LeftInverse, "for loop elements",
[ IsLoopElement ], [ IsLoopElmRep ],
x -> RightDivision( One( x ), x ) x -> RightDivision( One( x ), x )
); );
@ -248,12 +248,12 @@ InstallMethod( LeftInverse, "for loop elements",
## If <x> is a loop element, returns the left inverse of <x> ## If <x> is a loop element, returns the left inverse of <x>
InstallMethod( RightInverse, "for loop elements", InstallMethod( RightInverse, "for loop elements",
[ IsLoopElement ], [ IsLoopElmRep ],
x -> LeftDivision( x, One( x ) ) x -> LeftDivision( x, One( x ) )
); );
InstallMethod( InverseOp, "for loop elements", InstallMethod( InverseOp, "for loop elements",
[ IsLoopElement ], [ IsLoopElmRep ],
function( x ) function( x )
local y; local y;
y := RightInverse( x ); y := RightInverse( x );
@ -274,7 +274,7 @@ end );
## (xy)z = (x(yz))u. ## (xy)z = (x(yz))u.
InstallMethod( Associator, "for three quasigroup elements", InstallMethod( Associator, "for three quasigroup elements",
[ IsQuasigroupElement, IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y, z ) function( x, y, z )
return LeftDivision( x*(y*z), (x*y)*z ); return LeftDivision( x*(y*z), (x*y)*z );
end); end);
@ -288,7 +288,7 @@ end);
## (xy) = (yx)u. ## (xy) = (yx)u.
InstallMethod( Commutator, "for two quasigroup elements", InstallMethod( Commutator, "for two quasigroup elements",
[ IsQuasigroupElement, IsQuasigroupElement ], [ IsQuasigroupElmRep, IsQuasigroupElmRep ],
function( x, y ) function( x, y )
return LeftDivision( y*x, x*y ); return LeftDivision( y*x, x*y );
end); end);

View File

@ -1,104 +1,173 @@
############################################################################# #############################################################################
## ##
#W quasigroups.gd Representing, creating and displaying quasigroups [loops] #W quasigroups.gd Representing, creating and displaying quasigroups [loops]
## ##
#H @(#)$Id: quasigroups.gd, v 3.4.0 2017/10/17 gap Exp $ #H @(#)$Id: quasigroups.gd, v 3.4.0 2017/10/17 gap Exp $
## ##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary), #Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA) #Y P. Vojtechovsky (University of Denver, USA)
## ##
############################################################################# #############################################################################
## GAP CATEGORIES AND REPRESENTATIONS ## GAP CATEGORIES AND REPRESENTATIONS
## ------------------------------------------------------------------------- ## -------------------------------------------------------------------------
## element of a quasigroup ## Categories convenient for defining quasigroups
DeclareCategory( "IsQuasigroupElement", IsMultiplicativeElement );
DeclareRepresentation( "IsQuasigroupElmRep", ## element which is an admissible argument for the right argument of /
IsPositionalObjectRep and IsMultiplicativeElement, [1] ); DeclareCategory( "IsRightQuotientElement", IsExtLElement);
DeclareCategoryCollections("IsRightQuotientElement");
## element of a loop DeclareCategoryCollections("IsRightQuotientElementCollection");
DeclareCategory( "IsLoopElement",
IsQuasigroupElement and IsMultiplicativeElementWithInverse ); ## Every associative element with an inverse can form right quotients
DeclareRepresentation( "IsLoopElmRep", ## (in fact, in some sense it might be enough to have just a left inverse,
IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] ); ## but there doesn't seem to be any benefit to delving to that level of
## detail at this point.)
## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup) ## By noting this property, we can create a RightQuasigroup from, e.g., group
DeclareCategory( "IsLatinMagma", IsObject ); ## elements
InstallTrueMethod(IsRightQuotientElement,
## quasigroup IsMultiplicativeElementWithInverse and IsAssociativeElement);
DeclareCategory( "IsQuasigroup", IsMagma and IsLatinMagma );
## Now what we would like to do is re-declare
## loop ## DeclareOperation( "/", [IsExtRElement, IsRightQuotientElement] );
DeclareCategory( "IsLoop", IsQuasigroup and IsMultiplicativeElementWithInverseCollection); ## but we can't since "/" is in the kernel, so we will have to content
## ourselves with InstallOtherMethod() calls on /. (I am not actually sure what
############################################################################# ## the practical upshot of that is, i.e. if it has any shortcomings as compared
## TESTING MULTIPLICATION TABLES ## to if we could declare "/" more generally.)
## -------------------------------------------------------------------------
## Element which is admissible for the left argument of LeftQuotient()
DeclareOperation( "IsQuasigroupTable", [ IsMatrix ] ); DeclareCategory( "IsLeftQuotientElement", IsExtRElement);
DeclareSynonym( "IsQuasigroupCayleyTable", IsQuasigroupTable ); DeclareCategoryCollections("IsLeftQuotientElement");
DeclareOperation( "IsLoopTable", [ IsMatrix ] ); DeclareCategoryCollections("IsLeftQuotientElementCollection");
DeclareSynonym( "IsLoopCayleyTable", IsLoopTable );
DeclareOperation( "CanonicalCayleyTable", [ IsMatrix ] ); ## Every associative element with an inverse can form left quotients
DeclareOperation( "NormalizedQuasigroupTable", [ IsMatrix ] ); InstallTrueMethod(IsLeftQuotientElement,
IsMultiplicativeElementWithInverse and IsAssociativeElement);
#############################################################################
## CREATING QUASIGROUPS AND LOOPS MANUALLY ## Again, ideally (in some sense) we'd like to redeclare
## ------------------------------------------------------------------------- ## DeclareOperation("LeftQuotient", [IsLeftQuotientElement,IsExtLElement]);
DeclareAttribute( "CayleyTable", IsQuasigroup ); ## element of a quasigroup
DeclareOperation( "QuasigroupByCayleyTable", [ IsMatrix ] ); DeclareSynonym( "IsQuasigroupElement",
DeclareOperation( "LoopByCayleyTable", [ IsMatrix ] ); IsMultiplicativeElement and
DeclareOperation( "SetQuasigroupElmName", [ IsQuasigroup, IsString ] ); IsLeftQuotientElement and IsRightQuotientElement );
DeclareSynonym( "SetLoopElmName", SetQuasigroupElmName ); DeclareRepresentation( "IsQuasigroupElmRep",
DeclareOperation( "CanonicalCopy", [ IsQuasigroup ] ); IsPositionalObjectRep and IsQuasigroupElement, [1] );
############################################################################# ## element of a loop
## CREATING QUASIGROUPS AND LOOPS FROM A FILE DeclareSynonym( "IsLoopElement",
## ------------------------------------------------------------------------- IsQuasigroupElement and IsMultiplicativeElementWithInverse );
DeclareRepresentation( "IsLoopElmRep",
DeclareOperation( "QuasigroupFromFile", [ IsString, IsString ] ); IsQuasigroupElmRep and IsMultiplicativeElementWithInverse, [1] );
DeclareOperation( "LoopFromFile", [ IsString, IsString ] );
## Right quasigroup
############################################################################# DeclareCategory("IsRightQuasigroup",
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS IsMagma and IsRightQuotientElementCollection);
## -------------------------------------------------------------------------
## Although the following assertion is mathematically correct, unfortunately
DeclareOperation( "CayleyTableByPerms", [ IsPermCollection ] ); ## it interferes with method selection for standard group operations
DeclareOperation( "QuasigroupByLeftSection", [ IsPermCollection ] ); ## in GAP. As an example, if it is uncommented, it will no longer be possible
DeclareOperation( "LoopByLeftSection", [ IsPermCollection ] ); ## to construct a CyclicGroup; trying to do so eventually dies in
DeclareOperation( "QuasigroupByRightSection", [ IsPermCollection ] ); ## GeneratorsOfRightQuasigroup. Those errors could conceivably be corrected by
DeclareOperation( "LoopByRightSection", [ IsPermCollection ] ); ## delving further into GAP's method selection mechanism and adjusting the
DeclareOperation( "QuasigroupByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] ); ## declarations of various quasigroup operations, but it doesn't seem worth
DeclareOperation( "LoopByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] ); ## the effort as there is unlikely to be much call to consider a group as a
## quasigroup. If it is desirable to do so in a particular case, it should be
############################################################################# ## possible to use the elements of the group to form a quasigroup, since they
## CONVERSIONS ## will all satisfy IsRightQuotientElement by a TrueMethod installed above.
## -------------------------------------------------------------------------
## InstallTrueMethod(IsRightQuasigroup, IsGroup);
DeclareOperation( "IntoQuasigroup", [ IsMagma ] );
DeclareOperation( "PrincipalLoopIsotope", ## Left quasigroup
[ IsQuasigroup, IsQuasigroupElement, IsQuasigroupElement ] ); DeclareCategory("IsLeftQuasigroup",
DeclareOperation( "IntoLoop", [ IsMagma ] ); IsMagma and IsLeftQuotientElementCollection);
DeclareOperation( "IntoGroup", [ IsMagma ] );
## We forego the following for the reasons outlined above for right quasigroups.
#############################################################################
## PRODUCTS OF QUASIGROUPS AND LOOPS ## InstallTrueMethod(IsLeftQuasigroup, IsGroup);
## --------------------------------------------------------------------------
## quasigroup
#DirectProduct already declared for groups. DeclareSynonym( "IsQuasigroup", IsRightQuasigroup and IsLeftQuasigroup );
############################################################################# ## loop
## OPPOSITE QUASIGROUPS AND LOOPS DeclareSynonym( "IsLoop", IsQuasigroup and IsMagmaWithOne and
## -------------------------------------------------------------------------- IsMultiplicativeElementWithInverseCollection);
DeclareOperation( "OppositeQuasigroup", [ IsQuasigroup ] ); #############################################################################
DeclareOperation( "OppositeLoop", [ IsLoop ] ); ## TESTING MULTIPLICATION TABLES
DeclareAttribute( "Opposite", IsQuasigroup ); ## -------------------------------------------------------------------------
############################################################################# DeclareProperty( "IsLeftQuasigroupTable", IsMatrix );
## AUXILIARY DeclareProperty( "IsRightQuasigroupTable", IsMatrix );
## -------------------------------------------------------------------------- DeclareSynonym( "IsQuasigroupTable",
DeclareGlobalFunction( "LOOPS_ReadCayleyTableFromFile" ); IsLeftQuasigroupTable and IsRightQuasigroupTable );
DeclareGlobalFunction( "LOOPS_CayleyTableByRightFolder" ); DeclareSynonym( "IsQuasigroupCayleyTable", IsQuasigroupTable );
DeclareProperty( "IsLoopTable", IsMatrix );
DeclareSynonym( "IsLoopCayleyTable", IsLoopTable );
DeclareGlobalFunction("CanonicalCayleyTableOfLeftQuasigroupTable");
DeclareOperation( "CanonicalCayleyTable", [ IsMatrix ] );
DeclareOperation( "NormalizedQuasigroupTable", [ IsMatrix ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS MANUALLY
## -------------------------------------------------------------------------
DeclareAttribute( "CayleyTable", IsMagma );
DeclareOperation( "QuasigroupByCayleyTable", [ IsMatrix ] );
DeclareOperation( "LoopByCayleyTable", [ IsMatrix ] );
DeclareOperation( "SpecifyElmNamePrefix", [ IsCollection, IsString ] );
DeclareSynonym( "SetQuasigroupElmName", SpecifyElmNamePrefix );
DeclareSynonym( "SetLoopElmName", SpecifyElmNamePrefix );
DeclareOperation( "BindElmNames", [ IsMagma ] );
DeclareAttribute( "ConstructorFromTable", IsMagma );
DeclareOperation( "CanonicalCopy", [ IsMagma ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS FROM A FILE
## -------------------------------------------------------------------------
DeclareOperation( "QuasigroupFromFile", [ IsString, IsString ] );
DeclareOperation( "LoopFromFile", [ IsString, IsString ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS
## -------------------------------------------------------------------------
DeclareGlobalFunction("CayleyTableByPerms");
DeclareOperation( "QuasigroupByLeftSection", [ IsPermCollection ] );
DeclareOperation( "LoopByLeftSection", [ IsPermCollection ] );
DeclareOperation( "QuasigroupByRightSection", [ IsPermCollection ] );
DeclareOperation( "LoopByRightSection", [ IsPermCollection ] );
DeclareOperation( "QuasigroupByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] );
DeclareOperation( "LoopByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] );
#############################################################################
## CONVERSIONS
## -------------------------------------------------------------------------
DeclareOperation( "IntoQuasigroup", [ IsMagma ] );
DeclareOperation( "PrincipalLoopIsotope",
[ IsQuasigroup, IsQuasigroupElement, IsQuasigroupElement ] );
DeclareOperation( "IntoLoop", [ IsMagma ] );
DeclareOperation( "IntoGroup", [ IsMagma ] );
#############################################################################
## PRODUCTS OF QUASIGROUPS AND LOOPS
## --------------------------------------------------------------------------
DeclareGlobalFunction("ProductTableOfCanonicalCayleyTables");
#DirectProduct already declared for groups.
#############################################################################
## OPPOSITE QUASIGROUPS AND LOOPS
## --------------------------------------------------------------------------
DeclareGlobalFunction( "OppositeQuasigroup");
DeclareGlobalFunction( "OppositeLoop");
DeclareAttribute( "Opposite", IsMagma );
#############################################################################
## AUXILIARY
## --------------------------------------------------------------------------
DeclareGlobalFunction( "LOOPS_ReadCayleyTableFromFile" );
DeclareGlobalFunction( "LOOPS_CayleyTableByRightFolder" );

View File

@ -14,12 +14,12 @@
############################################################################# #############################################################################
## ##
#O IsQuasigroupTable( ls ) #O IsLeftQuasigroupTable( ls )
## ##
## Returns true if <ls> is an n by n latin square with n distinct ## Returns true if <ls> is an n by n matrix with n distinct
## integral entries. ## integral entries, each occurring exactly once in each row
InstallMethod( IsQuasigroupTable, "for matrix", InstallMethod( IsLeftQuasigroupTable, "for matrix",
[ IsMatrix ], [ IsMatrix ],
function( ls ) function( ls )
local first_row; local first_row;
@ -27,14 +27,21 @@ function( ls )
first_row := Set( ls[ 1 ] ); first_row := Set( ls[ 1 ] );
if not Length( first_row ) = Length( ls[ 1 ] ) then return false; fi; if not Length( first_row ) = Length( ls[ 1 ] ) then return false; fi;
if ForAll( ls, row -> Set( row ) = first_row ) = false then return false; fi; if ForAll( ls, row -> Set( row ) = first_row ) = false then return false; fi;
# checking columns
ls := TransposedMat( ls );
first_row := Set( ls[ 1 ] );
if not Length( first_row ) = Length( ls[ 1 ] ) then return false; fi;
if ForAll( ls, row -> Set( row ) = first_row ) = false then return false; fi;
return true; return true;
end ); end );
#############################################################################
##
#O IsRightQuasigroupTable( ls )
##
## Returns true if <ls> is an n by n matrix with n distinct
## integral entries, each occurring exactly once in each column
InstallMethod( IsRightQuasigroupTable, "for matrix",
[ IsMatrix ],
mat -> IsLeftQuasigroupTable(TransposedMat(mat))
);
############################################################################# #############################################################################
## ##
#O IsLoopTable( ls ) #O IsLoopTable( ls )
@ -51,6 +58,36 @@ function( ls )
and ls[ 1 ] = List( [1..Length(ls)], i -> ls[ i ][ 1 ] ); and ls[ 1 ] = List( [1..Length(ls)], i -> ls[ i ][ 1 ] );
end ); end );
#############################################################################
##
#O CanonicalCayleyTableOfLeftQuasigroupTable( ls )
##
## Returns a Cayley table isomorphic to <ls>, which must already be known
## to be a left quasigroup table, in which the entries of ls
## have been replaced by numerical values 1, ..., n in the following way:
## Let e_1 < ... < e_n be all distinct entries of ls. Then e_i is renamed
## to i. In particular, when {e_1,...e_n} = {1,...,n}, the operation
## does nothing.
InstallGlobalFunction( CanonicalCayleyTableOfLeftQuasigroupTable,
function( ls )
local n, entries, i, j, T;
n := Length( ls );
# finding all distinct entries in the table
entries := [];
for i in ls[1] do
AddSet( entries, i );
od;
if entries = [1..n] then return List(ls, l -> ShallowCopy(l));
fi;
# renaming the entries and making a mutable copy, too
T := List( [1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do
T[ i ][ j ] := Position( entries, ls[ i ][ j ] );
od; od;
return T;
end );
############################################################################# #############################################################################
## ##
#O CanonicalCayleyTable( ls ) #O CanonicalCayleyTable( ls )
@ -71,13 +108,15 @@ function( ls )
for i in [1..n] do for j in [1..n] do for i in [1..n] do for j in [1..n] do
AddSet( entries, ls[ i ][ j ] ); AddSet( entries, ls[ i ][ j ] );
od; od; od; od;
if entries = [1..n] then return List(ls, l -> ShallowCopy(l));
fi;
# renaming the entries and making a mutable copy, too # renaming the entries and making a mutable copy, too
T := List( [1..n], i -> [1..n] ); T := List( [1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do for i in [1..n] do for j in [1..n] do
T[ i ][ j ] := Position( entries, ls[ i ][ j ] ); T[ i ][ j ] := Position( entries, ls[ i ][ j ] );
od; od; od; od;
return T; return T;
end ); end);
############################################################################# #############################################################################
## ##
@ -94,7 +133,7 @@ function( ls )
Error( "LOOPS: <1> must be a latin square." ); Error( "LOOPS: <1> must be a latin square." );
fi; fi;
# renaming the entries to be 1, ..., n # renaming the entries to be 1, ..., n
T := CanonicalCayleyTable( ls ); T := CanonicalCayleyTableOfLeftQuasigroupTable( ls );
# permuting the columns so that the first row reads 1, ..., n # permuting the columns so that the first row reads 1, ..., n
perm := PermList( T[ 1 ] ); perm := PermList( T[ 1 ] );
T := List( T, row -> Permuted( row, perm ) ); T := List( T, row -> Permuted( row, perm ) );
@ -110,10 +149,12 @@ end );
## ##
#A CayleyTable( Q ) #A CayleyTable( Q )
## ##
## Returns the Cayley table of the quasigroup <Q> ## Returns the Cayley table of the magma <Q>. This is just like
## its multiplication table, except in case Q is a submagma of P, in which
## case the entries used are the indices in P rather than in Q.
InstallMethod( CayleyTable, "for quasigroup", InstallMethod( CayleyTable, "for magma",
[ IsQuasigroup ], [ IsMagma ],
function( Q ) function( Q )
local elms, parent_elms; local elms, parent_elms;
elms := Elements( Q ); elms := Elements( Q );
@ -136,7 +177,7 @@ function( ct )
Error( "LOOPS: <1> must be a latin square." ); Error( "LOOPS: <1> must be a latin square." );
fi; fi;
# Making sure that entries are 1, ..., n # Making sure that entries are 1, ..., n
ct := CanonicalCayleyTable( ct ); ct := CanonicalCayleyTableOfLeftQuasigroupTable( ct );
# constructing the family # constructing the family
F := NewFamily( "QuasigroupByCayleyTableFam", IsQuasigroupElement ); F := NewFamily( "QuasigroupByCayleyTableFam", IsQuasigroupElement );
# installing data for the family # installing data for the family
@ -146,7 +187,7 @@ function( ct )
NewType( F, IsQuasigroupElement and IsQuasigroupElmRep), [ i ] ) ) ); NewType( F, IsQuasigroupElement and IsQuasigroupElmRep), [ i ] ) ) );
F!.set := elms; F!.set := elms;
F!.cayleyTable := ct; F!.cayleyTable := ct;
F!.names := "q"; F!.elmNamePrefix := "q";
# creating the quasigroup # creating the quasigroup
Q := Objectify( NewType( FamilyObj( elms ), Q := Objectify( NewType( FamilyObj( elms ),
IsQuasigroup and IsAttributeStoringRep ), rec() ); IsQuasigroup and IsAttributeStoringRep ), rec() );
@ -155,6 +196,7 @@ function( ct )
SetAsSSortedList( Q, elms ); SetAsSSortedList( Q, elms );
SetParent( Q, Q ); SetParent( Q, Q );
SetCayleyTable( Q, ct ); SetCayleyTable( Q, ct );
SetConstructorFromTable(Q, QuasigroupByCayleyTable);
return Q; return Q;
end ); end );
@ -173,7 +215,7 @@ function( ct )
fi; fi;
# Making sure that the entries are 1, ..., n. # Making sure that the entries are 1, ..., n.
# The table will remain normalized. # The table will remain normalized.
ct := CanonicalCayleyTable( ct ); ct := CanonicalCayleyTableOfLeftQuasigroupTable( ct );
# constructing the family # constructing the family
F := NewFamily( "LoopByCayleyTableFam", IsLoopElement ); F := NewFamily( "LoopByCayleyTableFam", IsLoopElement );
# installing the data for the family # installing the data for the family
@ -183,7 +225,7 @@ function( ct )
NewType( F, IsLoopElement and IsLoopElmRep), [ i ] ) ) ); NewType( F, IsLoopElement and IsLoopElmRep), [ i ] ) ) );
F!.set := elms; F!.set := elms;
F!.cayleyTable := ct; F!.cayleyTable := ct;
F!.names := "l"; F!.elmNamePrefix := "l";
# creating the loop # creating the loop
L := Objectify( NewType( FamilyObj( elms ), L := Objectify( NewType( FamilyObj( elms ),
IsLoop and IsAttributeStoringRep ), rec() ); IsLoop and IsAttributeStoringRep ), rec() );
@ -193,39 +235,88 @@ function( ct )
SetParent( L, L ); SetParent( L, L );
SetCayleyTable( L, ct ); SetCayleyTable( L, ct );
SetOne( L, elms[ 1 ] ); SetOne( L, elms[ 1 ] );
SetConstructorFromTable(L, LoopByCayleyTable);
return L; return L;
end ); end );
############################################################################# #############################################################################
## ##
#O SetQuasigroupElmName( Q, name ) #O SpecifyElmNamePrefix( C, name )
## ##
## Changes the name of elements of quasigroup or loop <Q> to <name> ## Sets the elmNamePrefix property on the family of a Representative of
## collection <C>. For quasigroups, loops, and possibly related structures
## this changes the prefix with which the elements of that family are printed.
InstallMethod( SetQuasigroupElmName, "for quasigroup and string", InstallMethod( SpecifyElmNamePrefix, "for collection and string",
[ IsQuasigroup, IsString ], [ IsCollection, IsString ],
function( Q, name ) function( Q, name )
local F; local F;
F := FamilyObj( Elements( Q )[ 1 ] ); F := FamilyObj( Representative( Q ) );
F!.names := name; F!.elmNamePrefix := name;
end);
#############################################################################
##
#O BindElmNames( M )
##
## For each element e of the magma <M>, binds the identifier named String(e)
## to e.
InstallMethod( BindElmNames, "for a magma",
[ IsMagma ],
function( M )
local e, nm;
for e in Elements(M) do
nm := String(e);
BindGlobal(nm, e);
MakeReadWriteGlobal(nm);
od;
return;
end); end);
#############################################################################
##
#O ConstructorFromTable( M )
##
## Given a magma <M>, returns a function which will create a domain of the
## same structure as M from from an operation table. This implementation of
## the method is to backfill the constructors for library domains that do
## not set the attribute directly at construction time. New domains that wish
## to use facilities like CanonicalCopy or Opposite should call
## SetConstructorFromTable at creation time.
InstallMethod( ConstructorFromTable, "for other magmas",
[ IsMagma ],
function ( M )
# Go in reverse order of refinement of structure
if IsGroup(M) then
return GroupByMultiplicationTable;
elif IsMagmaWithInverses(M) then
return MagmaWithInversesByMultiplicationTable;
elif IsMonoid(M) then
return MonoidByMultiplicationTable;
elif IsMagmaWithOne(M) then
return MagmaWithOneByMultiplicationTable;
elif IsSemigroup(M) then
return SemigroupByMultiplicationTable;
fi;
return MagmaByMultiplicationTable;
end);
############################################################################# #############################################################################
## ##
#O CanonicalCopy( Q ) #O CanonicalCopy( Q )
## ##
## Returns a canonical copy of <Q>, that is, an isomorphic object <O> with ## Returns a canonical copy of <Q>, that is, an isomorphic object <O> with
## canonical multiplication table and Parent( <O> ) = <O>. ## canonical multiplication table and no parent set. Note that this is
## guaranteed to be a new object, not satisfying IsIdenticalObj with any
## previously existing structure. THEREFORE:
## (PROG) Properties and attributes are lost! ## (PROG) Properties and attributes are lost!
InstallMethod( CanonicalCopy, "for quasigroup or loop", InstallMethod( CanonicalCopy, "for magma",
[ IsQuasigroup ], [ IsMagma ],
function( Q ) M -> ConstructorFromTable(M)(MultiplicationTable(M))
if IsLoop( Q ) then );
return LoopByCayleyTable( CayleyTable( Q ) );
fi;
return QuasigroupByCayleyTable( CayleyTable( Q ) );
end);
############################################################################# #############################################################################
@ -347,30 +438,44 @@ end );
############################################################################# #############################################################################
## ##
#O CayleyTableByPerms( perms ) #O CayleyTableByPerms( perms, [X] )
## ##
## Given a set <perms> of n permutations of an n-element set X, returns ## Given a set <perms> of n permutations of an n-element set <X> of natural
## n by n Cayley table ct such that ct[i][j] = X[j]^perms[i]. ## numbers, returns an n by n Cayley table ct such that
## The operation is safe only if at most one permutation of <perms> is ## ct[i][j] = X[j]^perms[i].
## the identity permutation, and all other permutations of <perms> ##
## move all points of X. ## Note that the argument <X> is optional, and if omitted, the function will
## assume that at most one permutation of <perms> is the identity
## permutation, and that all other permutations of <perms>
## move all points of <X>.
InstallMethod( CayleyTableByPerms, InstallGlobalFunction( CayleyTableByPerms,
"for a list of permutations", function( perms, rest... )
[ IsPermCollection ],
function( perms )
local n, pts, max; local n, pts, max;
n := Length( perms ); n := Length( perms );
if n=1 then if n=1 then
return [ [ 1 ] ]; return [ [ 1 ] ];
fi; fi;
# one of perms[ 1 ], perms[ 2 ] must move all points # one of perms[ 1 ], perms[ 2 ] must move all points
pts := MovedPoints( perms[ 2 ] ); if Length(rest) > 0 then
if pts = [] then pts := rest[1];
else
pts := MovedPoints( perms[ 2 ] );
if pts = [] then
pts := MovedPoints( perms[ 1 ] ); pts := MovedPoints( perms[ 1 ] );
fi;
fi;
if Length(pts) <> n then
Error("perms for cayley table of size ", n, " cannot act on ",
Length(pts), " points.");
fi; fi;
max := Maximum( pts ); max := Maximum( pts );
# we permute the whole interval [1..max] and then keep only those coordinates corresponding to pts if max = n then
# Common case, we are permuting [1..n]
return List( perms, x -> ListPerm(x, n));
fi;
# Otherwise we permute the whole interval [1..max] and then keep only those coordinates corresponding to pts
return List( perms, p -> Permuted( [1..max], p^(-1) ){ pts } ); return List( perms, p -> Permuted( [1..max], p^(-1) ){ pts } );
end); end);
@ -627,95 +732,104 @@ end);
# groups in GAP. The idea is as follows: # groups in GAP. The idea is as follows:
# We want to calculate direct product of quasigroups, loops and groups. # We want to calculate direct product of quasigroups, loops and groups.
# If only groups are on the list, standard GAP DirectProduct will take care # 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, # of it. If there are also some quasigroups or loops on the list (but nothing
# we must take care of it. # that is not a quasigroup), we must take care of it.
# However, we do not know if such a list will be processed with # However, we do not know if such a list will be processed with
# DirectProductOp( <IsList>, <IsGroup> ), or # DirectProductOp( <IsList>, <IsGroup> ), or
# DirectProductOp( <IsList>, <IsQuasigroup> ), # DirectProductOp( <IsList>, <IsQuasigroup> ),
# since this depends on which algebra is listed first. # 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( <IsList>, <IsGroup> )", InstallMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsGroup> )",
[ IsList, IsGroup], [ IsList, IsGroup],
function( list, first ) function( list, first )
local L, p; local L, p;
# Check the arguments. # Check the arguments.
if IsEmpty( list ) then Error( "LOOPS: <1> must be nonempty." ); fi; if IsEmpty( list ) then
if not ForAny( list, IsQuasigroup ) then Error( "LOOPS: <1> must be nonempty." );
# there are no quasigroups or loops on the list elif Length(list) = 1 then
TryNextMethod(); return list[1];
fi; fi;
if ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then for p in [1..Length(list)] do
# there are other objects beside groups, loops and quasigroups on the list if not IsGroup(list[p]) then
TryNextMethod(); return DirectProductOp(Permuted(list, (1,p)), list[p]);
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;
fi; fi;
od; 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 i, nL, nM, TL, TM, T, j, k, s;
TL := tablist[1];
for s in [2..Length(tablist)] 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); end);
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )", InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )",
[ IsList, IsQuasigroup ], [ IsList, IsQuasigroup ],
function( list, dummy ) function( list, dummy )
local group_list, quasigroup_list, group_product, are_all_loops, local group_list, quasigroup_list, group_product, are_all_loops, n, i, T;
n, i, nL, nM, TL, TM, T, j, k, s;
# check the arguments # check the arguments
if IsEmpty( list ) then if IsEmpty( list ) then
Error( "LOOPS: <1> must be nonempty." ); Error( "LOOPS: <1> must be nonempty." );
elif ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then elif Length(list) = 1 then
TryNextMethod(); return list[1];
fi; fi;
group_list := [];
quasigroup_list := list{[1]};
are_all_loops := IsLoop(list[1]);
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 # only groups, quasigroups and loops are on the list, with at least one
group_list := Filtered( list, G -> IsGroup( G ) ); # non-group; moreover, we have partitioned the list into groups and
quasigroup_list := Filtered( list, G -> IsQuasigroup( G ) ); # non-groups and checked whether all quasigroups are really loops.
if not IsEmpty( group_list ) then # some groups are on the list if not IsEmpty( group_list ) then # some groups are on the list
group_product := DirectProductOp( group_list, group_list[ 1 ] ); group_product := DirectProductOp( group_list, group_list[ 1 ] );
Add( quasigroup_list, IntoLoop( group_product ) ); Add( quasigroup_list, IntoLoop( group_product ) );
fi; 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 ); n := Length( quasigroup_list );
if n=1 then # We will not use recursion; start by making all Cayley tables canonical
return quasigroup_list[ 1 ]; Apply(quasigroup_list,
fi; Q -> CanonicalCayleyTableOfLeftQuasigroupTable( CayleyTable( Q ) ) );
# at least 2 quasigroups and loops; we will not use recursion T := ProductTableOfCanonicalCayleyTables( quasigroup_list );
# making all Cayley tables cannonical
for s in [1..n] do
quasigroup_list[ s ] := QuasigroupByCayleyTable( CanonicalCayleyTable( CayleyTable( quasigroup_list[ s ] ) ) );
od;
for s in [2..n] do
nL := Size( quasigroup_list[ 1 ] );
nM := Size( quasigroup_list[ s ] );
TL := CayleyTable( quasigroup_list[ 1 ] );
TM := CayleyTable( 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 ] := QuasigroupByCayleyTable( T );
od;
if are_all_loops then if are_all_loops then
return IntoLoop( quasigroup_list[1] ); return LoopByCayleyTable( T );
fi; fi;
return quasigroup_list[ 1 ]; return QuasigroupByCayleyTable( T );
end ); end );
############################################################################# #############################################################################
@ -726,41 +840,36 @@ function( list, dummy )
## ##
#O OppositeQuasigroup( Q ) #O OppositeQuasigroup( Q )
## ##
## Returns the quasigroup opposite to the quasigroup <Q>. ## Identical to Opposite, except forces its return to be a quasigroup if
## possible
InstallMethod( OppositeQuasigroup, "for quasigroup", InstallGlobalFunction( OppositeQuasigroup,
[ IsQuasigroup ], Q -> IntoQuasigroup( Opposite( Q ) ) );
function( Q )
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
############################################################################# #############################################################################
## ##
#O OppositeLoop( Q ) #O OppositeLoop( Q )
## ##
## Returns the loop opposite to the loop <Q>. ## Identical to Opposite, except forces its return to be a loop if possible
InstallMethod( OppositeLoop, "for loop", InstallGlobalFunction( OppositeLoop, L -> IntoLoop( Opposite( L ) ) );
[ IsLoop ],
function( Q )
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
############################################################################# #############################################################################
## ##
#A Opposite( Q ) #A Opposite( M )
## ##
## Returns the quasigroup opposite to the quasigroup <Q>. When ## Returns the magma opposite to the magma <M>, with as much structure
## <Q> is a loop, a loop is returned. ## as can be preserved.
InstallMethod( Opposite, "for magma",
[ IsMagma and HasMultiplicationTable],
M -> ConstructorFromTable(M)( TransposedMat( MultiplicationTable( M ) ) )
);
InstallMethod( Opposite, "for quasigroup", InstallMethod( Opposite, "for quasigroup",
[ IsQuasigroup ], [ IsQuasigroup ], # Might not have a multiplication table
function( Q ) M -> ConstructorFromTable(M)( TransposedMat( CayleyTable( M ) ) )
if IsLoop( Q ) then );
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
fi;
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
############################################################################# #############################################################################
## DISPLAYING QUASIGROUPS AND LOOPS ## DISPLAYING QUASIGROUPS AND LOOPS