Copy of LOOPS 3.3.0

This commit is contained in:
Glen Whitney 2017-10-16 21:43:09 +02:00
commit 7e8b3b5562
510 changed files with 97978 additions and 0 deletions

21
gap/banner.g Normal file
View file

@ -0,0 +1,21 @@
#############################################################################
##
#A banner.g loops G. P. Nagy / P. Vojtechovsky
##
#H @(#)$Id: banner.g, v 3.3.0 2016/09/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
if not QUIET and BANNER then
Print(
" ______________________________________________________\n",
" LOOPS: Computing with quasigroups and loops in GAP \n",
" version 3.3.0 \n",
" Gabor P. Nagy & Petr Vojtechovsky \n",
" nagyg@math.u-szeged.hu petr@math.du.edu \n",
" ------------------------------------------------------\n",
" homepage: www.math.du.edu/loops \n",
" ______________________________________________________\n\n");
fi;

18
gap/bol_core_methods.gd Normal file
View file

@ -0,0 +1,18 @@
#############################################################################
##
#W bol_core_methods.gd Common methods for Bol loops [loops]
##
#H @(#)$Id: bol_core_methods.gd, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareAttribute( "AssociatedLeftBruckLoop", IsLeftBolLoop );
DeclareAttribute( "AssociatedRightBruckLoop", IsRightBolLoop );
DeclareOperation( "IsExactGroupFactorization", [ IsGroup, IsGroup, IsGroup ] );
DeclareGlobalFunction( "RightBolLoopByExactGroupFactorizationNC" ); # auxiliary
DeclareGlobalFunction( "RightBolLoopByExactGroupFactorization" ); # variable arguments

133
gap/bol_core_methods.gi Normal file
View file

@ -0,0 +1,133 @@
#############################################################################
##
#W bol_core_methods.gi Common methods for Bol loops [loops]
##
#H @(#)$Id: bol_core_methods.gi, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#A AssociatedLeftBruckLoop( Q )
##
## Given a left Bol loop Q for which x -> x^2 is a permutation,
## returns the associated left Bruck loop, defined by
## x o y = (x*((y*y)*x))^(1/2) (default) or
## x o y = x^(1/2)*(y*x^(1/2)) (commented out)
InstallMethod( AssociatedLeftBruckLoop,
[ IsLeftBolLoop ],
function( Q )
local n, ct, squares, roots, new_ct, L, i, j;
n := Size( Q );
ct := CanonicalCayleyTable( CayleyTable( Q ) );
squares := List( [ 1..n ], i -> ct[i][i] );
if not Size( Set( squares ) ) = n then
Error( "LOOPS: <1> must be a Bol loop in which squaring is a bijection." );
fi;
roots := Inverse( PermList( squares ) ); # square roots
new_ct := List([1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do
new_ct[i][j] := (ct[i][ct[ct[j][j]][i]])^roots; # (x*((y*y)*x))^(1/2)
# new_ct[i][j] := ct[i^roots][ct[ j ][ i^roots ]]; # x^(1/2)*(y*x^(1/2))
od; od;
L := LoopByCayleyTable( new_ct ); # the associated left Bruck loop
SetIsLeftBruckLoop( L, true );
return L;
end );
#############################################################################
##
#A AssociatedRightBruckLoop( Q )
##
## Given a right Bol loop Q for which x -> x^2 is a permutation,
## returns the associated right Bruck loop, defined by
## x o y = ((x*(y*y))*x)^(1/2) (default) or
## x o y = (x^(1/2)*y)*x^(1/2) (commented out)
InstallMethod( AssociatedRightBruckLoop,
[ IsRightBolLoop ],
function( Q )
local n, ct, squares, roots, new_ct, L, i, j;
n := Size( Q );
ct := CanonicalCayleyTable( CayleyTable( Q ) );
squares := List( [ 1..n ], i -> ct[i][i] );
if not Size( Set( squares ) ) = n then
Error( "LOOPS: <1> must be a right Bol loop in which squaring is a bijection." );
fi;
roots := Inverse( PermList( squares ) ); # square roots
new_ct := List([1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do
new_ct[i][j] := (ct[ct[j][ct[i][i]]][j])^roots; # x o y = ((y*(x*x))*y)^(1/2)
# new_ct[i][j] := ct[ct[j^roots][i]][j^roots]; # x o y = (y^(1/2)*x)*y^(1/2)
od; od;
L := LoopByCayleyTable( new_ct ); # the associated right Bruck loop
SetIsRightBruckLoop( L, true );
return L;
end );
#############################################################################
##
#O IsExactGroupFactorization( G, H1, H2 )
##
## Let G be a group and H_1, H_2 subgroups. The triple (G,H_1,H_2) is an
## exact group factorization, if H_1 \cap H_2 = 1 and G=H_1H_2.
##
## Returns true if (G, H1, H2) is an exact group factorization.
InstallMethod( IsExactGroupFactorization, "for a group and two subgroups",
[ IsGroup, IsGroup, IsGroup ],
function( G, H1, H2 )
return IsSubgroup(G,H1) and IsSubgroup(G,H2) and Size(G)=Size(H1)*Size(H2) and IsTrivial(Intersection(H1,H2));
end);
#############################################################################
##
#F RightBolLoopByExactGroupFactorization
##
## Let (G,H_1,H_2) be an exact group factorization. Define
## U = G\times G, S = H_1 \times H_2, and T = {(g,g^{-1}) : g in G }.
## Then (U,S,T) is a right Bol loop folder
## Returns the right Bol loop corresponding to (U,S,T).
InstallGlobalFunction( RightBolLoopByExactGroupFactorizationNC,
function( g, h1, h2 )
local f,sect,stab,ghom,st,q,rmlt;
f := DirectProduct( g, g );
sect := List( g, x -> Image( Embedding( f, 1 ), x )*Image( Embedding( f, 2 ), x^-1 ) );
stab := ClosureGroup( Image( Embedding( f, 1 ), h1 ), Image( Embedding( f, 2 ), h2 ) );
ghom := ActionHomomorphism( f, RightCosets( f, stab ), OnRight );
st := Image( ghom, sect );;
q := LoopByRightSection( st );
SetRightSection( q, st );
rmlt := Subgroup( f, sect );
SetRightMultiplicationGroup( q, Image( ghom, Subgroup( f, SmallGeneratingSet( rmlt ) ) ) );
return q;
end);
InstallGlobalFunction( RightBolLoopByExactGroupFactorization,
function( arg )
local g, h1, h2;
if Length( arg ) = 3 then
g := arg[1]; h1 := arg[2]; h2 := arg[3];
elif Length( arg ) = 2 then
g := arg[1]; h1 := arg[2]; h2 := Stabilizer( g, Orbit( g )[1] );
elif Length( arg ) = 1 and Length( arg[1] ) = 3 then
g := arg[1][1]; h1 := arg[1][2]; h2 := arg[1][3];
elif Length(arg) = 1 and Length( arg[1] ) = 2 then
g := arg[1][1]; h1 := arg[1][2]; h2 := Stabilizer(g,Orbit(g)[1]);
else
Error("LOOPS: Argument must be an exact group factorization or a group with a regular subgroup.");
fi;
if not IsExactGroupFactorization( g, h1, h2 ) then
Error("LOOPS: Argument does not correspond to an exact group factorization.");
fi;
return RightBolLoopByExactGroupFactorizationNC( g, h1, h2 );
end);

106
gap/classes.gd Normal file
View file

@ -0,0 +1,106 @@
#############################################################################
##
#W classes.gd Testing varieties [loops]
##
#H @(#)$Id: classes.gd, v 3.3.0 2016/10/26 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## ASSOCIATIVITY, COMMUTATIVITY AND GENERALIZATIONS
## -------------------------------------------------------------------------
DeclareProperty( "IsAssociative", IsLoop );
DeclareProperty( "IsCommutative", IsQuasigroup );
DeclareProperty( "IsPowerAssociative", IsQuasigroup );
DeclareProperty( "IsDiassociative", IsQuasigroup );
#############################################################################
## INVERSE PROPERTIES
## -------------------------------------------------------------------------
DeclareProperty( "HasLeftInverseProperty", IsLoop );
DeclareProperty( "HasRightInverseProperty", IsLoop );
DeclareProperty( "HasInverseProperty", IsLoop );
DeclareProperty( "HasWeakInverseProperty", IsLoop );
DeclareProperty( "HasTwosidedInverses", IsLoop );
DeclareProperty( "HasAutomorphicInverseProperty", IsLoop );
DeclareProperty( "HasAntiautomorphicInverseProperty", IsLoop );
#############################################################################
## PROPERTIES OF QUASIGROUPS
## -------------------------------------------------------------------------
DeclareProperty( "IsSemisymmetric", IsQuasigroup );
DeclareProperty( "IsTotallySymmetric", IsQuasigroup );
DeclareProperty( "IsIdempotent", IsQuasigroup );
DeclareProperty( "IsSteinerQuasigroup", IsQuasigroup );
DeclareProperty( "IsUnipotent", IsQuasigroup );
DeclareProperty( "IsLDistributive", IsQuasigroup );
DeclareProperty( "IsRDistributive", IsQuasigroup );
DeclareSynonymAttr( "IsLeftDistributive", IsLDistributive );
DeclareSynonymAttr( "IsRightDistributive", IsRDistributive );
#IsDistributive already declared in GAP
DeclareProperty( "IsEntropic", IsQuasigroup );
DeclareSynonymAttr( "IsMedial", IsEntropic );
#############################################################################
## LOOPS OF BOL-MOUFANG TYPE
## -------------------------------------------------------------------------
DeclareProperty( "IsExtraLoop", IsLoop );
DeclareProperty( "IsMoufangLoop", IsLoop );
DeclareProperty( "IsCLoop", IsLoop );
DeclareProperty( "IsLeftBolLoop", IsLoop );
DeclareProperty( "IsRightBolLoop", IsLoop );
DeclareProperty( "IsLCLoop", IsLoop );
DeclareProperty( "IsRCLoop", IsLoop );
DeclareProperty( "IsLeftNuclearSquareLoop", IsLoop );
DeclareProperty( "IsMiddleNuclearSquareLoop", IsLoop );
DeclareProperty( "IsRightNuclearSquareLoop", IsLoop );
DeclareProperty( "IsNuclearSquareLoop", IsLoop );
DeclareProperty( "IsFlexible", IsQuasigroup );
DeclareProperty( "IsLeftAlternative", IsQuasigroup );
DeclareProperty( "IsRightAlternative", IsQuasigroup );
DeclareProperty( "IsAlternative", IsQuasigroup );
#############################################################################
## POWER ALTERNATIVE LOOPS
## -------------------------------------------------------------------------
DeclareProperty( "IsLeftPowerAlternative", IsLoop );
DeclareProperty( "IsRightPowerAlternative", IsLoop );
DeclareProperty( "IsPowerAlternative", IsLoop );
#############################################################################
## CC-LOOPS AND RELATED PROPERTIES
## -------------------------------------------------------------------------
DeclareProperty( "IsLCCLoop", IsLoop );
DeclareSynonymAttr( "IsLeftConjugacyClosedLoop", IsLCCLoop );
DeclareProperty( "IsRCCLoop", IsLoop );
DeclareSynonymAttr( "IsRightConjugacyClosedLoop", IsRCCLoop );
DeclareProperty( "IsCCLoop", IsLoop );
DeclareSynonymAttr( "IsConjugacyClosedLoop", IsCCLoop );
DeclareProperty( "IsOsbornLoop", IsLoop );
############################################################################
## ADDITIONAL VARIETIES OF LOOPS
## -------------------------------------------------------------------------
DeclareProperty( "IsCodeLoop", IsLoop );
DeclareProperty( "IsSteinerLoop", IsLoop );
DeclareProperty( "IsLeftBruckLoop", IsLoop );
DeclareSynonymAttr( "IsLeftKLoop", IsLeftBruckLoop );
DeclareProperty( "IsRightBruckLoop", IsLoop );
DeclareSynonymAttr( "IsRightKLoop", IsRightBruckLoop );
DeclareProperty( "IsALoop", IsLoop );
DeclareSynonymAttr( "IsAutomorphicLoop", IsALoop );
DeclareProperty( "IsLeftALoop", IsLoop );
DeclareSynonymAttr( "IsLeftAutomorphicLoop", IsLeftALoop );
DeclareProperty( "IsMiddleALoop", IsLoop );
DeclareSynonymAttr( "IsMiddleAutomorphicLoop", IsMiddleALoop );
DeclareProperty( "IsRightALoop", IsLoop );
DeclareSynonymAttr( "IsRightAutomorphicLoop", IsRightALoop );

916
gap/classes.gi Normal file
View file

@ -0,0 +1,916 @@
#############################################################################
##
#W classes.gi Testing properties/varieties [loops]
##
#H @(#)$Id: classes.gi, v 3.3.0 2016/10/26 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## ASSOCIATIVITY, COMMUTATIVITY AND GENERALIZATIONS
## -------------------------------------------------------------------------
# (PROG) IsAssociative is already implemented for magmas, but we provide
# a new method based on sections. This new method is much faster for groups,
# and a bit slower for nonassociative loops.
InstallOtherMethod( IsAssociative, "for loops",
[ IsLoop ], 0,
function( Q )
local sLS, x, y;
sLS := Set( LeftSection( Q ) );
for x in LeftSection( Q ) do
for y in LeftSection( Q ) do
if not x*y in sLS then return false; fi;
od;
od;
return true;
end);
# implies
InstallTrueMethod( IsExtraLoop, IsAssociative and IsLoop );
#############################################################################
##
#P IsCommutative( Q )
##
## Returns true if <Q> is commutative.
InstallOtherMethod( IsCommutative, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return LeftSection( Q ) = RightSection( Q );
end );
#############################################################################
##
#P IsPowerAssociative( Q )
##
## Returns true if <Q> is a power associative quasigroup.
InstallOtherMethod( IsPowerAssociative, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local checked, x, S;
checked := [];
repeat
x := Difference( Elements(Q), checked );
if IsEmpty( x ) then
return true;
fi;
S := Subquasigroup( Q, [x[1]] );
if not IsAssociative( S ) then
return false;
fi;
checked := Union( checked, Elements( S ) ); # S is a group, so every subquasigroup of S is a group
until 0=1;
end );
# implies
InstallTrueMethod( HasTwosidedInverses, IsPowerAssociative and IsLoop );
#############################################################################
##
#P IsDiassociative( Q )
##
## Returns true if <Q> is a diassociative quasigroup.
InstallOtherMethod( IsDiassociative, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local checked, all_pairs, x, S;
checked := [];
all_pairs := Combinations( PosInParent( Elements( Q ) ), 2 ); # it is faster to work with integers
repeat
x := Difference( all_pairs, checked );
if IsEmpty( x ) then
return true;
fi;
S := Subquasigroup( Q, x[1] );
if not IsAssociative( S ) then
return false;
fi;
checked := Union( checked, Combinations( PosInParent( Elements( S ) ), 2 ) );
until 0=1;
end );
# implies
InstallTrueMethod( IsPowerAlternative, IsDiassociative );
InstallTrueMethod( IsFlexible, IsDiassociative );
#############################################################################
## INVERSE PROPERTIES
## -------------------------------------------------------------------------
#############################################################################
##
#P HasLeftInverseProperty( L )
##
## Returns true if <L> has the left inverse property.
InstallMethod( HasLeftInverseProperty, "for loop",
[ IsLoop ],
function( L )
return ForAll( LeftSection( L ), a -> a^-1 in LeftSection( L ) );
end );
#############################################################################
##
#P HasRightInverseProperty( L )
##
## Returns true if <L> has the right inverse property.
InstallMethod( HasRightInverseProperty, "for loop",
[ IsLoop ],
function( L )
return ForAll( RightSection( L ), a -> a^-1 in RightSection( L ) );
end );
#############################################################################
##
#P HasInverseProperty( L )
##
## Returns true if <L> has the inverse property.
InstallMethod( HasInverseProperty, "for loop",
[ IsLoop ],
function( L )
return HasLeftInverseProperty( L ) and HasRightInverseProperty( L );
end );
#############################################################################
##
#P HasWeakInverseProperty( L )
##
## Returns true if <L> has the weak inverse property.
InstallMethod( HasWeakInverseProperty, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> ForAll( L, y -> LeftInverse(x*y)*x=LeftInverse(y) ));
end );
#############################################################################
##
#P HasTwosidedInverses( L )
##
## Returns true if <L> has two-sided inverses.
InstallMethod( HasTwosidedInverses, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> LeftInverse( x ) = RightInverse( x ) );
end );
#############################################################################
##
#P HasAutomorphicInverseProperty( L )
##
## Returns true if <L> has the automorphic inverse property.
InstallMethod( HasAutomorphicInverseProperty, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> ForAll( L, y ->
LeftInverse( x*y ) = LeftInverse( x )*LeftInverse( y ) ) );
end );
#############################################################################
##
#P HasAntiautomorphicInverseProperty( L )
##
## Returns true if <L> has the antiautomorphic inverse property.
InstallMethod( HasAntiautomorphicInverseProperty, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> ForAll( L, y ->
LeftInverse( x*y ) = LeftInverse( y )*LeftInverse( x ) ) );
end );
# implies and is implied by (for inverse properties)
InstallTrueMethod( HasAntiautomorphicInverseProperty, HasAutomorphicInverseProperty and IsCommutative );
InstallTrueMethod( HasAutomorphicInverseProperty, HasAntiautomorphicInverseProperty and IsCommutative );
InstallTrueMethod( HasLeftInverseProperty, HasInverseProperty );
InstallTrueMethod( HasRightInverseProperty, HasInverseProperty );
InstallTrueMethod( HasWeakInverseProperty, HasInverseProperty );
InstallTrueMethod( HasAntiautomorphicInverseProperty, HasInverseProperty );
InstallTrueMethod( HasTwosidedInverses, HasAntiautomorphicInverseProperty );
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and IsCommutative );
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and IsCommutative );
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasRightInverseProperty );
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasWeakInverseProperty );
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and HasWeakInverseProperty );
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasAntiautomorphicInverseProperty );
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and HasAntiautomorphicInverseProperty );
InstallTrueMethod( HasInverseProperty, HasWeakInverseProperty and HasAntiautomorphicInverseProperty );
InstallTrueMethod( HasTwosidedInverses, HasLeftInverseProperty );
InstallTrueMethod( HasTwosidedInverses, HasRightInverseProperty );
InstallTrueMethod( HasTwosidedInverses, IsFlexible and IsLoop );
#############################################################################
## PROPERTIES OF QUASIGROUPS
## -------------------------------------------------------------------------
#############################################################################
##
#P IsSemisymmetric( Q )
##
## Returns true if the quasigroup <Q> is semisymmetric, i.e., (xy)x=y.
InstallMethod( IsSemisymmetric, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return ForAll( Q, x ->
LeftTranslation( Q, x ) * RightTranslation( Q, x ) = () );
end );
#############################################################################
##
#P IsTotallySymmetric( Q )
##
## Returns true if the quasigroup <Q> is totally symmetric, i.e,
## commutative and semisymmetric.
InstallMethod( IsTotallySymmetric, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return IsCommutative( Q ) and IsSemisymmetric( Q );
end );
#############################################################################
##
#P IsIdempotent( Q )
##
## Returns true if the quasigroup <Q> is idempotent, i.e., x*x=x.
InstallMethod( IsIdempotent, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return ForAll( Q, x -> x*x = x );
end );
#############################################################################
##
#P IsSteinerQuasigroup( Q )
##
## Returns true if the quasigroup <Q> is a Steiner quasigroup, i.e.,
## idempotent and totally symmetric.
InstallMethod( IsSteinerQuasigroup, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return IsIdempotent( Q ) and IsTotallySymmetric( Q );
end );
#############################################################################
##
#P IsUnipotent( Q )
##
## Returns true if the quasigroup <Q> is unipotent, i.e., x*x=y*y.
InstallMethod( IsUnipotent, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local square;
if IsLoop( Q ) then return IsIdempotent( Q ); fi;
square := Elements( Q )[ 1 ]^2;
return ForAll( Q, y -> y^2 = square );
end );
#############################################################################
##
#P IsLDistributive( Q )
##
## Returns true if the quasigroup <Q> is left distributive.
InstallOtherMethod( IsLDistributive, "for Quasigroup",
[ IsQuasigroup ],
function( Q )
# BETTER ALGORITHM LATER?
local x, y, z;
for x in Q do for y in Q do for z in Q do
if not x*(y*z) = (x*y)*(x*z) then return false; fi;
od; od; od;
return true;
end );
#############################################################################
##
#P IsRDistributive( Q )
##
## Returns true if the quasigroup <Q> is right distributive.
InstallOtherMethod( IsRDistributive, "for Quasigroup",
[ IsQuasigroup ],
function( Q )
# BETTER ALGORITHM LATER?
local x, y, z;
for x in Q do for y in Q do for z in Q do
if not (x*y)*z = (x*z)*(y*z) then return false; fi;
od; od; od;
return true;
end );
#############################################################################
##
#P IsEntropic( Q )
##
## Returns true if the quasigroup <Q> is entropic.
InstallMethod( IsEntropic, "for quasigroup",
[ IsQuasigroup ],
function( Q )
# BETTER ALGORITHM LATER?
local x, y, z, w;
for x in Q do for y in Q do for z in Q do for w in Q do
if not (x*y)*(z*w) = (x*z)*(y*w) then return false; fi;
od; od; od; od;
return true;
end );
#############################################################################
## LOOPS OF BOL-MOUFANG
## -------------------------------------------------------------------------
#############################################################################
##
#P IsExtraLoop( L )
##
## Returns true if <L> is an extra loop.
InstallMethod( IsExtraLoop, "for loop",
[ IsLoop ],
function( L )
return IsMoufangLoop( L ) and IsNuclearSquareLoop( L );
end );
# implies
InstallTrueMethod( IsMoufangLoop, IsExtraLoop );
InstallTrueMethod( IsCLoop, IsExtraLoop );
# is implied by
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsLeftNuclearSquareLoop );
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsMiddleNuclearSquareLoop );
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsRightNuclearSquareLoop );
#############################################################################
##
#P IsMoufangLoop( L )
##
## Returns true if <L> is a Moufang loop.
InstallMethod( IsMoufangLoop, "for loop",
[ IsLoop ],
function( L )
return IsLeftBolLoop( L ) and HasRightInverseProperty( L );
end );
# implies
InstallTrueMethod( IsLeftBolLoop, IsMoufangLoop );
InstallTrueMethod( IsRightBolLoop, IsMoufangLoop );
InstallTrueMethod( IsDiassociative, IsMoufangLoop );
# is implied by
InstallTrueMethod( IsMoufangLoop, IsLeftBolLoop and IsRightBolLoop );
#############################################################################
##
#P IsCLoop( L )
##
## Returns true if <L> is a C-loop.
InstallMethod( IsCLoop, "for loop",
[ IsLoop ],
function( L )
return IsLCLoop( L ) and IsRCLoop( L );
end );
# implies
InstallTrueMethod( IsLCLoop, IsCLoop );
InstallTrueMethod( IsRCLoop, IsCLoop );
InstallTrueMethod( IsDiassociative, IsCLoop and IsFlexible);
# is implied by
InstallTrueMethod( IsCLoop, IsLCLoop and IsRCLoop );
#############################################################################
##
#P IsLeftBolLoop( L )
##
## Returns true if <L> is a left Bol loop.
InstallMethod( IsLeftBolLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( LeftSection( L ), a ->
ForAll( LeftSection( L ), b -> a*b*a in LeftSection( L ) ) );
end );
# implies
InstallTrueMethod( IsRightBolLoop, IsLeftBolLoop and IsCommutative );
InstallTrueMethod( IsLeftPowerAlternative, IsLeftBolLoop );
#############################################################################
##
#P IsRightBolLoop( L )
##
## Returns true if <L> is a right Bol loop.
InstallMethod( IsRightBolLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( RightSection( L ), a ->
ForAll( RightSection( L ), b -> a*b*a in RightSection( L ) ) );
end );
# implies
InstallTrueMethod( IsLeftBolLoop, IsRightBolLoop and IsCommutative );
InstallTrueMethod( IsRightPowerAlternative, IsRightBolLoop );
#############################################################################
##
#P IsLCLoop( L )
##
## Returns true if <L> is an LC-loop.
InstallMethod( IsLCLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( LeftSection( L ), a ->
ForAll( RightSection( L ), b -> b^(-1)*a*a*b in LeftSection( L ) ) );
end );
# implies
InstallTrueMethod( IsLeftPowerAlternative, IsLCLoop );
InstallTrueMethod( IsLeftNuclearSquareLoop, IsLCLoop );
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsLCLoop );
InstallTrueMethod( IsRCLoop, IsLCLoop and IsCommutative );
#############################################################################
##
#P IsRCLoop( L )
##
## Returns true if <L> is an RC-loop.
InstallMethod( IsRCLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( LeftSection( L ), a ->
ForAll( RightSection( L ), b -> a^(-1)*b*b*a in RightSection( L ) ) );
end );
# implies
InstallTrueMethod( IsRightPowerAlternative, IsRCLoop );
InstallTrueMethod( IsRightNuclearSquareLoop, IsRCLoop );
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsRCLoop );
InstallTrueMethod( IsLCLoop, IsRCLoop and IsCommutative );
#############################################################################
##
#P IsLeftNuclearSquareLoop( L )
##
## Returns true if <L> is a left nuclear square loop.
InstallMethod( IsLeftNuclearSquareLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> x^2 in LeftNucleus( L ) );
end );
#implies
InstallTrueMethod( IsRightNuclearSquareLoop, IsLeftNuclearSquareLoop and IsCommutative );
#############################################################################
##
#P IsMiddleNuclearSquareLoop( L )
##
## Returns true if <L> is a middle nuclear square loop.
InstallMethod( IsMiddleNuclearSquareLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> x^2 in MiddleNucleus( L ) );
end );
#############################################################################
##
#P IsRightNuclearSquareLoop( L )
##
## Returns true if <L> is a right nuclear square loop.
InstallMethod( IsRightNuclearSquareLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( L, x -> x^2 in RightNucleus( L ) );
end );
# implies
InstallTrueMethod( IsLeftNuclearSquareLoop, IsRightNuclearSquareLoop and IsCommutative );
#############################################################################
##
#P IsNuclearSquareLoop( L )
##
## Returns true if <L> is a nuclear square loop.
InstallMethod( IsNuclearSquareLoop, "for loop",
[ IsLoop ],
function( L )
return IsLeftNuclearSquareLoop( L ) and IsRightNuclearSquareLoop( L )
and IsMiddleNuclearSquareLoop( L );
end );
# implies
InstallTrueMethod( IsLeftNuclearSquareLoop, IsNuclearSquareLoop );
InstallTrueMethod( IsRightNuclearSquareLoop, IsNuclearSquareLoop );
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsNuclearSquareLoop );
# is implied by
InstallTrueMethod( IsNuclearSquareLoop, IsLeftNuclearSquareLoop
and IsRightNuclearSquareLoop and IsMiddleNuclearSquareLoop );
#############################################################################
##
#P IsFlexible( Q )
##
## Returns true if <Q> is a flexible quasigroup.
InstallMethod( IsFlexible, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local LS, RS;
LS := LeftSection( Q );
RS := RightSection( Q );
return ForAll( [1..Size( Q )], i -> LS[ i ] * RS[ i ] = RS[ i ] * LS[ i ] );
end );
# is implied by
InstallTrueMethod( IsFlexible, IsCommutative );
#############################################################################
##
#P IsLeftAlternative( Q )
##
## Returns true if <Q> is a left alternative quasigroup.
InstallMethod( IsLeftAlternative, "for quasigroup",
[ IsQuasigroup],
function( Q )
if IsLoop( Q ) then
return ForAll( LeftSection( Q ), a -> a*a in LeftSection( Q ) );
fi;
return ForAll( Q, x -> ForAll( Q, y -> x*(x*y) = (x*x)*y ) );
end );
# implies
InstallTrueMethod( IsRightAlternative, IsLeftAlternative and IsCommutative );
#############################################################################
##
#P IsRightAlternative( Q )
##
## Returns true if <Q> is a right alternative quasigroup.
InstallMethod( IsRightAlternative, "for quasigroup",
[ IsQuasigroup ],
function( Q )
if IsLoop( Q ) then
return ForAll( RightSection( Q ), a -> a*a in RightSection( Q ) );
fi;
return ForAll( Q, x -> ForAll( Q, y -> (x*y)*y = x*(y*y) ) );
end );
# implies
InstallTrueMethod( IsLeftAlternative, IsRightAlternative and IsCommutative );
#############################################################################
##
#P IsAlternative( Q )
##
## Returns true if <Q> is an alternative quasigroup.
InstallMethod( IsAlternative, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return IsLeftAlternative( Q ) and IsRightAlternative( Q );
end );
# implies
InstallTrueMethod( IsLeftAlternative, IsAlternative );
InstallTrueMethod( IsRightAlternative, IsAlternative );
# is implied by
InstallTrueMethod( IsAlternative, IsLeftAlternative and IsRightAlternative );
#############################################################################
## POWER ALTERNATIVE LOOPS
## -------------------------------------------------------------------------
#############################################################################
##
#P IsLeftPowerAlternative( L )
##
## Returns true if <L> is a left power alternative loop.
InstallMethod( IsLeftPowerAlternative, "for loop",
[ IsLoop ],
function( L )
local i, M;
if Size( L ) = 1 then return true; fi;
for i in [ 2..Size( L )] do
M := Subloop( L, [ Elements( L )[ i ] ]);
if not Size( RelativeLeftMultiplicationGroup( L, M ) ) = Size( M ) then
return false;
fi;
od;
return true;
end );
# implies
InstallTrueMethod( IsLeftAlternative, IsLeftPowerAlternative );
InstallTrueMethod( HasLeftInverseProperty, IsLeftPowerAlternative );
InstallTrueMethod( IsPowerAssociative, IsLeftPowerAlternative );
#############################################################################
##
#P IsRightPowerAlternative( L )
##
## Returns true if <L> is a right power alternative loop.
InstallMethod( IsRightPowerAlternative, "for loop",
[ IsLoop ],
function( L )
local i, M;
if Size( L ) = 1 then return true; fi;
for i in [ 2..Size( L ) ] do
M := Subloop( L, [ Elements( L )[ i ] ] );
if not Size( RelativeRightMultiplicationGroup( L, M ) ) = Size( M ) then
return false;
fi;
od;
return true;
end );
# implies
InstallTrueMethod( IsRightAlternative, IsRightPowerAlternative );
InstallTrueMethod( HasRightInverseProperty, IsRightPowerAlternative );
InstallTrueMethod( IsPowerAssociative, IsRightPowerAlternative );
#############################################################################
##
#P IsPowerAlternative( L )
##
## Returns true if <L> is a power alternative loop.
InstallMethod( IsPowerAlternative, "for loop",
[ IsLoop ],
function( L )
return ( IsLeftPowerAlternative( L ) and IsRightPowerAlternative( L ) );
end );
# implies
InstallTrueMethod( IsLeftPowerAlternative, IsPowerAlternative );
InstallTrueMethod( IsRightPowerAlternative, IsPowerAlternative );
#############################################################################
## CC-LOOPS AND RELATED PROPERTIES
## -------------------------------------------------------------------------
#############################################################################
##
#P IsLCCLoop( L )
##
## Returns true if <L> is a left conjugacy closed loop.
InstallMethod( IsLCCLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( LeftSection( L ), a ->
ForAll( LeftSection( L ), b -> b*a*b^(-1) in LeftSection( L ) ) );
end );
# implies
InstallTrueMethod( IsAssociative, IsLCCLoop and IsCommutative );
InstallTrueMethod( IsExtraLoop, IsLCCLoop and IsMoufangLoop );
#############################################################################
##
#P IsRCCLoop( L )
##
## Returns true if <L> is a right conjugacy closed loop.
InstallMethod( IsRCCLoop, "for loop",
[ IsLoop ],
function( L )
return ForAll( RightSection( L ), a ->
ForAll( RightSection( L ), b -> b*a*b^(-1) in RightSection( L ) ) );
end );
# implies
InstallTrueMethod( IsAssociative, IsRCCLoop and IsCommutative );
InstallTrueMethod( IsExtraLoop, IsRCCLoop and IsMoufangLoop );
#############################################################################
##
#P IsCCLoop( L )
##
## Returns true if <L> is a conjugacy closed loop.
InstallMethod( IsCCLoop, "for loop",
[ IsLoop ],
function( L )
return IsLCCLoop( L ) and IsRCCLoop( L );
end );
# implies
InstallTrueMethod( IsLCCLoop, IsCCLoop );
InstallTrueMethod( IsRCCLoop, IsCCLoop );
# is implied by
InstallTrueMethod( IsCCLoop, IsLCCLoop and IsRCCLoop );
#############################################################################
##
#P IsOsbornLoop( L )
##
## Returns true if <L> is an Osborn loop.
InstallMethod( IsOsbornLoop, "for loop",
[ IsLoop ],
function( L )
# MIGHT REDO LATER
return ForAll(L, x-> ForAll(L, y->
ForAll(L, z-> x*((y*z)*x) = LeftDivision(LeftInverse(x),y)*(z*x))
));
end );
# is implied by
InstallTrueMethod( IsOsbornLoop, IsMoufangLoop );
InstallTrueMethod( IsOsbornLoop, IsCCLoop );
#############################################################################
## ADDITIONAL VARIETIES OF LOOPS
## -------------------------------------------------------------------------
#############################################################################
##
#P IsCodeLoop( L )
##
## Returns true if <L> is an even code loop.
InstallMethod( IsCodeLoop, "for loop",
[ IsLoop ],
function( L )
# even code loops are precisely Moufang 2-loops with Frattini subloop of order 1, 2
return Set( Factors( Size( L ) ) ) = [ 2 ]
and IsMoufangLoop( L )
and Size( FrattiniSubloop( L ) ) in [1, 2];
end );
# implies
InstallTrueMethod( IsExtraLoop, IsCodeLoop );
InstallTrueMethod( IsCCLoop, IsCodeLoop );
#############################################################################
##
#P IsSteinerLoop( L )
##
## Returns true if <L> is a Steiner loop.
InstallMethod( IsSteinerLoop, "for loop",
[ IsLoop ],
function( L )
# Steiner loops are inverse property loops of exponent at most 2.
return HasInverseProperty( L ) and Exponent( L )<=2;
end );
# implies
InstallTrueMethod( IsCommutative, IsSteinerLoop );
InstallTrueMethod( IsCLoop, IsSteinerLoop );
#############################################################################
##
#P IsLeftBruckLoop( L )
##
## Returns true if <L> is a left Bruck loop.
InstallMethod( IsLeftBruckLoop, "for loop",
[ IsLoop ],
function( L )
return IsLeftBolLoop( L ) and HasAutomorphicInverseProperty( L );
end );
# implies
InstallTrueMethod( HasAutomorphicInverseProperty, IsLeftBruckLoop );
InstallTrueMethod( IsLeftBolLoop, IsLeftBruckLoop );
InstallTrueMethod( IsRightBruckLoop, IsLeftBruckLoop and IsCommutative );
# is implied by
InstallTrueMethod( IsLeftBruckLoop, IsLeftBolLoop and HasAutomorphicInverseProperty );
#############################################################################
##
#P IsRightBruckLoop( L )
##
## Returns true if <L> is a right Bruck loop.
InstallMethod( IsRightBruckLoop, "for loop",
[ IsLoop ],
function( L )
return IsRightBolLoop( L ) and HasAutomorphicInverseProperty( L );
end );
# implies
InstallTrueMethod( HasAutomorphicInverseProperty, IsRightBruckLoop );
InstallTrueMethod( IsRightBolLoop, IsRightBruckLoop );
InstallTrueMethod( IsLeftBruckLoop, IsRightBruckLoop and IsCommutative );
# is implied by
InstallTrueMethod( IsRightBruckLoop, IsRightBolLoop and HasAutomorphicInverseProperty );
#############################################################################
##
#P IsLeftALoop( L )
##
## Returns true if <L> is a left A-loop, that is if
## all left inner mappings are automorphisms of <L>.
InstallMethod( IsLeftALoop, "for loop",
[ IsLoop ],
function( L )
local gens;
gens := GeneratorsOfGroup( LeftInnerMappingGroup( L ) );
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
end);
#############################################################################
##
#P IsRightALoop( L )
##
## Returns true if <L> is a right A-loop, that is if
## all right inner mappings are automorphisms of <L>.
InstallMethod( IsRightALoop, "for loop",
[ IsLoop ],
function( L )
local gens;
gens := GeneratorsOfGroup( RightInnerMappingGroup( L ) );
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
end);
#############################################################################
##
#P IsMiddleALoop( L )
##
## Returns true if <L> is a middle A-loop, that is if
## all middle inner mappings (conjugations) are automorphisms of <L>.
InstallMethod( IsMiddleALoop, "for loop",
[ IsLoop ],
function( L )
local gens;
gens := GeneratorsOfGroup( MiddleInnerMappingGroup( L ) );
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
end);
#############################################################################
##
#P IsALoop( L )
##
## Returns true if <L> is an A-loop, that is if
## all inner mappings are automorphisms of <L>.
InstallMethod( IsALoop, "for loop",
[ IsLoop ],
function( Q )
return IsLeftALoop(Q) and IsRightALoop(Q) and IsMiddleALoop(Q);
end);
# implies
InstallTrueMethod( IsLeftALoop, IsALoop );
InstallTrueMethod( IsRightALoop, IsALoop );
InstallTrueMethod( IsMiddleALoop, IsALoop );
InstallTrueMethod( IsMiddleALoop, IsCommutative );
InstallTrueMethod( IsALoop, IsLeftALoop and IsCommutative );
InstallTrueMethod( IsALoop, IsRightALoop and IsCommutative );
InstallTrueMethod( IsLeftALoop, IsRightALoop and HasAntiautomorphicInverseProperty );
InstallTrueMethod( IsRightALoop, IsLeftALoop and HasAntiautomorphicInverseProperty );
InstallTrueMethod( IsFlexible, IsMiddleALoop );
InstallTrueMethod( HasAntiautomorphicInverseProperty, IsFlexible and IsLeftALoop );
InstallTrueMethod( HasAntiautomorphicInverseProperty, IsFlexible and IsRightALoop );
InstallTrueMethod( IsMoufangLoop, IsALoop and IsLeftAlternative );
InstallTrueMethod( IsMoufangLoop, IsALoop and IsRightAlternative );
InstallTrueMethod( IsMoufangLoop, IsALoop and HasLeftInverseProperty );
InstallTrueMethod( IsMoufangLoop, IsALoop and HasRightInverseProperty );
InstallTrueMethod( IsMoufangLoop, IsALoop and HasWeakInverseProperty );
# is implied by
InstallTrueMethod( IsLeftALoop, IsLeftBruckLoop );
InstallTrueMethod( IsLeftALoop, IsLCCLoop );
InstallTrueMethod( IsRightALoop, IsRightBruckLoop );
InstallTrueMethod( IsRightALoop, IsRCCLoop );
InstallTrueMethod( IsALoop, IsCommutative and IsMoufangLoop );

20
gap/convert.gd Normal file
View file

@ -0,0 +1,20 @@
############################################################################
##
#W convert.gd Converting between numerical bases [loops]
##
#H @(#)$Id: convert.gd, v 3.3.0 2016/10/19 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
# auxiliary
DeclareOperation( "LOOPS_DigitToChar", [ IsInt ] );
DeclareOperation( "LOOPS_CharToDigit", [ IsChar ] );
DeclareOperation( "LOOPS_EncodeCayleyTable", [ IsList ] );
DeclareOperation( "LOOPS_DecodeCayleyTable", [ IsString ] );
DeclareOperation( "LOOPS_ConvertToDecimal", [ IsString, IsInt ] );
DeclareGlobalFunction( "LOOPS_ConvertFromDecimal" ); # has variable number of arguments
DeclareGlobalFunction( "LOOPS_ConvertBase" ); # has variable number of arguments
DeclareOperation( "LOOPS_EncodeCocycle", [ IsList, IsList ] );
DeclareOperation( "LOOPS_DecodeCocycle", [ IsList, IsList ] );

272
gap/convert.gi Normal file
View file

@ -0,0 +1,272 @@
#############################################################################
##
#W convert.gi Conversions, encoding and decoding [loops]
##
#H @(#)$Id: convert.gi, v 3.0.0 2015/06/02 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
############################################################################
##
## CONVERSIONS BETWEEN CHARACTERS AND "DIGITS" IN BASE 91.
## -------------------------------------------------------------------------
##
## Suitable characters to represent digits in GAP are found in the interval
## CHAR_INT(35)..CHAR_INT(126), except for CHAR_INT[92] = '\\'.
## This leads to natural base 91 = 126-35.
## To implement binary, decimal and hexadecimal numbers naturally,
## we reorder the suitable 91 characters to read as follows:
BindGlobal(
"LOOPS_conversion_string",
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%&'()*+,-./:;<=>?@[]^_`{|}~"
);
#############################################################################
##
#O LOOPS_DigitToChar( d )
##
## Converts an integer <d> in the range [0..90] to a character.
InstallMethod( LOOPS_DigitToChar, "for integer",
[ IsInt ],
function( d )
return LOOPS_conversion_string[ d + 1 ];
end );
#############################################################################
##
#O LOOPS_CharToDigit( c )
##
## Converts a character <c> to an integer in the range [0..90].
InstallMethod( LOOPS_CharToDigit, "for character",
[ IsChar ],
function( c )
return Position( LOOPS_conversion_string, c ) - 1;
end );
#############################################################################
##
#O LOOPS_EncodeCayleyTable( ct )
#O LOOPS_DecodeCayleyTable( str )
##
## Auxiliary routines for encoding and decoding of loop Cayley tables
## of order in [1..91], using characters instead of numbers.
## When n>2, first row and column are ignored because they can be
## reconstructed from the rest of the table.
## When <ct> is commutative, only "half" of the table is saved.
## This can be detected from <str> and decoded appropriately.
InstallMethod( LOOPS_EncodeCayleyTable, "for list",
[ IsList ],
function( ct )
local n, ret, start, i, j;
n := Length( ct );
ret := "";
start := 2;
# small cases
if n<3 then
start := 1;
fi;
if n>91 then
Error("LOOPS: Encoding of Cayley tables is supported only for order less than 92.");
fi;
ret := "";
if ct <> TransposedMat( ct ) then # general case
for i in [start..n] do for j in [start..n] do
Add(ret, LOOPS_DigitToChar( ct[i][j]-1 ) );
od; od;
else # commutative case
for i in [start..n] do for j in [i..n] do
Add(ret, LOOPS_DigitToChar( ct[i][j]-1 ) );
od; od;
fi;
return ret;
end );
InstallMethod( LOOPS_DecodeCayleyTable, "for string",
[ IsString ],
function( str )
local symbols, n, pos, ret, i, j;
symbols := Set( Set( str ), c -> LOOPS_CharToDigit(c) + 1 );
if Length(str)=1 then
return [[LOOPS_CharToDigit(str[1])+1]];
fi;
if Length(str)=3 and Size(symbols)=2 then # n=2 (automatically commutative)
return [
[LOOPS_CharToDigit(str[1])+1, LOOPS_CharToDigit(str[2])+1],
[LOOPS_CharToDigit(str[2])+1, LOOPS_CharToDigit(str[3])+1]
];
fi;
# n>2
n := Size( symbols ); # number of distinct symbols
if n>91 then
Error("LOOPS: Decoding of Cayley tables is supported only for order less than 92.");
fi;
ret := List([1..n], i -> List( [1..n], j -> -1 ) );
# the table except for the first row and first column
pos := 1;
if Length(str) = (n-1)^2 then # noncommutative case
for i in [2..n] do for j in [2..n] do
ret[i][j] := LOOPS_CharToDigit( str[pos] ) + 1;
pos := pos+1;
od; od;
else # commutative case
for i in [2..n] do for j in [i..n] do
ret[i][j] := LOOPS_CharToDigit( str[pos] ) + 1;
ret[j][i] := ret[i][j];
pos := pos + 1;
od; od;
fi;
# determining the first row and first column
for i in [2..n] do
ret[i][1] := Difference( symbols, ret[i] )[1];
ret[1][i] := Difference( symbols, List( [2..n], j->ret[j][i] ) )[1];
od;
ret[1][1] := Difference( symbols, ret[1] )[1];
return ret;
end );
#############################################################################
##
#O LOOPS_ConvertToDecimal( s, n )
##
## Converts an <n>-ary number represented by a string <s> to a decimal
## number represented as integer.
InstallMethod( LOOPS_ConvertToDecimal, "for string and integer",
[ IsString, IsInt ],
function( s, n )
local ls, d, i;
ls := Length( s );
d := 0;
for i in [1..ls] do
d := d + LOOPS_CharToDigit(s[i])*(n^(ls-i));
od;
return d;
end );
#############################################################################
##
#F LOOPS_ConvertFromDecimal( arg )
##
## arg = [ <d>, <m>, optional <k> ]
## Converts a decimal number <d> to a number in base <m>.
## Optional parameter <k> is the minimal required number of "digits"
## of the output in new base <m>, including zeros at the beginning
## Returns the corresponding number as a string in base <m> (of length at least <k>).
InstallGlobalFunction( LOOPS_ConvertFromDecimal, function( arg )
local d, m, s, r, prefix_s;
d := arg[1];
m := arg[2];
s := "";
while d>0 do
r := d mod m;
Add( s, LOOPS_DigitToChar( r ) );
d := (d-r)/m;
od;
s := Reversed( s );
if Length( arg ) > 2 then
prefix_s := List( [1..arg[3]-Length(s)], i -> LOOPS_DigitToChar( 0 ) );
s := Concatenation( prefix_s, s );
fi;
return s;
end );
#############################################################################
##
#F LOOPS_ConvertBase( arg )
##
## arg = [ s, n, m, optional k ]
## s is a string that represents a number in base n
## m is a new base
## optional parameter k is the minimal required number of "digits"
## of the output in new base m, including zeros at the beginning
## Returns the corresponding number in base m (with at least k digits).
InstallGlobalFunction( LOOPS_ConvertBase, function( arg )
local d;
d := LOOPS_ConvertToDecimal( arg[1], arg[2] );
if Length(arg)>3 then
return LOOPS_ConvertFromDecimal( d, arg[3], arg[4] );
fi;
return LOOPS_ConvertFromDecimal( d, arg[3] );
end );
#############################################################################
##
#O LOOPS_EncodeCocycle( coc, values )
#O LOOPS_DecodeCocycle( ecoc, values )
##
## Given loops F and K, a cocycle is a mapping from F x F to K.
## Let n=|F| and b=|K|.
## Cocycles are represented as n x n arrays with entries in a b-element set.
##
## These are auxiliary routines for encoding and decoding of cocycles.
## <coc> is a cocycle, an n x n matrix
## <values> is a set of cardinality b
## every entry in <coc> must lie in <values> but not vice versa
## <ecoc> is an encoded cocycle, a list of the form [n, is_comm, data],
## where n=|F|, is_comm is true iff <coc> is commutative, and
## data is an encoded cocycle table
## Note: The encoded cocycle has default values in [0..b-1]. The argument
## <values> can be used to populate the cocycle with other values.
InstallMethod( LOOPS_EncodeCocycle, "for two lists",
[ IsList, IsList ],
function( coc, values )
local b, n, is_commutative, ret, i, start, j;
b := Length( values );
if not b < 92 then
Error("LOOPS: Encoding of cocycles is supported only for loops of order less than 92.");
fi;
n := Length(coc);
is_commutative := coc = TransposedMat(coc);
ret := [ n, is_commutative, "" ];
for i in [1..n] do
start := 1;
if is_commutative then start := i; fi;
for j in [start..n] do
Add( ret[3], LOOPS_DigitToChar( Position( values, coc[i][j] ) - 1 ) );
od;
od;
ret[3] := LOOPS_ConvertBase( ret[3], b, 91 );
return ret;
end);
InstallMethod( LOOPS_DecodeCocycle, "for two lists",
[ IsList, IsList ],
function( ecoc, values )
local n, is_commutative, b, s, coc, pos, i, j;
n := ecoc[1];
is_commutative := ecoc[2];
b := Length( values );
if is_commutative then
s := LOOPS_ConvertBase( ecoc[3], 91, b, n*(n+1)/2 );
else
s := LOOPS_ConvertBase( ecoc[3], 91, b, n^2 );
fi;
coc := List([1..n], i -> [1..n]);
pos := 1;
if is_commutative then
for i in [1..n] do for j in [i..n] do
coc[i][j] := values[ LOOPS_CharToDigit( s[pos] ) + 1 ];
coc[j][i] := coc[i][j];
pos := pos + 1;
od; od;
else
for i in [1..n] do for j in [1..n] do
coc[i][j] := values[ LOOPS_CharToDigit( s[pos] ) + 1 ];
pos := pos + 1;
od; od;
fi;
return coc;
end);

126
gap/core_methods.gd Normal file
View file

@ -0,0 +1,126 @@
#############################################################################
##
#W core_methods.gd Most common structural methods [loops]
##
#H @(#)$Id: core_methods.gd, v 3.3.0 2016/09/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## GENERATORS
## -------------------------------------------------------------------------
DeclareSynonymAttr( "GeneratorsOfQuasigroup", GeneratorsOfMagma );
DeclareSynonymAttr( "GeneratorsOfLoop", GeneratorsOfQuasigroup );
# attribute GeneratorsSmallest already declared for groups
# attribite SmallGeneratingSet already declared for groups
#############################################################################
## SECTIONS
## -------------------------------------------------------------------------
DeclareAttribute( "LeftSection", IsQuasigroup );
DeclareAttribute( "RightSection", IsQuasigroup );
DeclareOperation( "LeftTranslation",
[ IsQuasigroup, IsQuasigroupElement ] );
DeclareOperation( "RightTranslation",
[ IsQuasigroup, IsQuasigroupElement ] );
#############################################################################
## MULTIPLICATION GROUPS
## -------------------------------------------------------------------------
DeclareAttribute( "LeftMultiplicationGroup", IsQuasigroup );
DeclareAttribute( "RightMultiplicationGroup", IsQuasigroup );
DeclareAttribute( "MultiplicationGroup", IsQuasigroup );
DeclareOperation( "RelativeRightMultiplicationGroup",
[ IsQuasigroup, IsQuasigroup ] );
DeclareOperation( "RelativeLeftMultiplicationGroup",
[ IsQuasigroup, IsQuasigroup ] );
DeclareOperation( "RelativeMultiplicationGroup",
[ IsQuasigroup, IsQuasigroup ] );
#############################################################################
## INNER MAPPING GROUPS
## -------------------------------------------------------------------------
DeclareOperation( "LeftInnerMapping", [ IsLoop, IsLoopElement, IsLoopElement ] );
DeclareOperation( "RightInnerMapping", [ IsLoop, IsLoopElement, IsLoopElement ] );
DeclareOperation( "MiddleInnerMapping", [ IsLoop, IsLoopElement ] );
DeclareAttribute( "InnerMappingGroup", IsLoop );
DeclareAttribute( "LeftInnerMappingGroup", IsLoop );
DeclareAttribute( "MiddleInnerMappingGroup", IsLoop );
DeclareAttribute( "RightInnerMappingGroup", IsLoop );
#############################################################################
## SUBQUASIGROUPS AND SUBLOOPS
## -------------------------------------------------------------------------
DeclareOperation( "PosInParent", [ IsCollection ] );
# Position already declared in GAP
DeclareOperation( "SubquasigroupNC", [ IsQuasigroup, IsSet ] );
DeclareOperation( "Subquasigroup", [ IsQuasigroup, IsList ] );
DeclareOperation( "Subloop", [ IsLoop, IsList ] );
DeclareOperation( "IsSubquasigroup", [ IsQuasigroup, IsQuasigroup ] );
DeclareOperation( "IsSubloop", [ IsLoop, IsLoop ] );
DeclareOperation( "AllSubquasigroups", [ IsQuasigroup ] );
DeclareOperation( "AllSubloops", [ IsLoop ] );
# RightCosets already declared in GAP
DeclareOperation( "RightTransversal", [ IsLoop, IsLoop ] );
#############################################################################
## NUCLEUS, COMMUTANT, CENTER
## -------------------------------------------------------------------------
DeclareAttribute( "LeftNucleus", IsQuasigroup );
DeclareAttribute( "RightNucleus", IsQuasigroup );
DeclareAttribute( "MiddleNucleus", IsQuasigroup );
DeclareAttribute( "Nuc", IsQuasigroup );
DeclareSynonymAttr( "NucleusOfLoop", Nuc );
DeclareSynonymAttr( "NucleusOfQuasigroup", Nuc );
DeclareAttribute( "Commutant", IsQuasigroup );
DeclareAttribute( "Center", IsQuasigroup );
DeclareAttribute( "AssociatorSubloop", IsLoop );
#############################################################################
## EXPONENT
## -------------------------------------------------------------------------
DeclareAttribute( "Exponent", IsLoop );
#############################################################################
## NORMALITY
## -------------------------------------------------------------------------
InParentFOA( "IsNormal", IsLoop, IsLoop, DeclareProperty );
InParentFOA( "NormalClosure", IsLoop, IsLoop, DeclareAttribute );
# IsSimple is already declared for groups
#############################################################################
## FACTOR LOOP
## -------------------------------------------------------------------------
DeclareOperation( "FactorLoop", [ IsLoop, IsLoop ] );
DeclareOperation( "NaturalHomomorphismByNormalSubloop", [ IsLoop, IsLoop ] );
#############################################################################
## NILPOTENCY
## -------------------------------------------------------------------------
DeclareAttribute( "NilpotencyClassOfLoop", IsLoop );
# IsNilpotent is already declared for groups
DeclareAttribute( "IsStronglyNilpotent", IsLoop );
# UpperCentralSeries already declared for groups.
# LowerCentralSeries already declared for groups.
#############################################################################
## SOLVABILITY
## -------------------------------------------------------------------------
# IsSolvable is already declared for groups
DeclareAttribute( "DerivedSubloop", IsLoop );
# DerivedLength already declared for groups
DeclareAttribute( "FrattiniSubloop", IsLoop );
# FrattinifactorSize already declared for groups

1124
gap/core_methods.gi Normal file

File diff suppressed because it is too large Load diff

37
gap/elements.gd Normal file
View file

@ -0,0 +1,37 @@
#############################################################################
##
#W elements.gd Elements and basic arithmetic operations [loops]
##
#H @(#)$Id: quasigroups.gd, v 2.0.0 2008/01/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## DIVISION
## -------------------------------------------------------------------------
DeclareOperation( "LeftDivision",
[ IsQuasigroupElement, IsQuasigroupElement ] );
DeclareOperation( "RightDivision",
[ IsQuasigroupElement, IsQuasigroupElement ] );
DeclareOperation( "LeftDivisionCayleyTable", [ IsQuasigroup ] );
DeclareOperation( "RightDivisionCayleyTable", [ IsQuasigroup ] );
#############################################################################
## POWERS AND INVERSES
## -------------------------------------------------------------------------
DeclareAttribute( "One", IsLoopElement );
DeclareAttribute( "RightInverse", IsLoopElement );
DeclareAttribute( "LeftInverse", IsLoopElement );
#############################################################################
## ASSOCIATORS AND COMMUTATORS
## -------------------------------------------------------------------------
DeclareOperation( "Associator",
[ IsQuasigroupElement, IsQuasigroupElement, IsQuasigroupElement ] );
DeclareOperation( "Commutator",
[ IsQuasigroupElement, IsQuasigroupElement ] );

294
gap/elements.gi Normal file
View file

@ -0,0 +1,294 @@
#############################################################################
##
#W elements.gd Elements and basic arithmetic operations [loops]
##
#H @(#)$Id: quasigroups.gd, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## DISPLAYING AND COMPARING ELEMENTS
## -------------------------------------------------------------------------
InstallMethod( PrintObj, "for a quasigroup element",
[ IsQuasigroupElement ],
function( obj )
local F;
F := FamilyObj( obj );
Print( F!.names, obj![ 1 ] );
end );
InstallMethod( PrintObj, "for a loop element",
[ IsLoopElement ],
function( obj )
local F;
F := FamilyObj( obj );
Print( F!.names, obj![ 1 ] );
end );
InstallMethod( \=, "for two elements of a quasigroup",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] = y![ 1 ];
end );
InstallMethod( \<, "for two elements of a quasigroup",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] < y![ 1 ];
end );
InstallMethod( \., "for quasigroup and positive integer",
[ IsQuasigroup, IsPosInt ],
function( Q, k )
return GeneratorsOfQuasigroup( Q )[ Int( NameRNam( k ) ) ];
end );
#############################################################################
## MULTIPLICATION
## -------------------------------------------------------------------------
## Multiplication without parentheses is evaluated from left to right,
## i.e., a*b*c=(a*b)*c. Powers use binary decomposition.
InstallMethod( \*, "for two quasigroup elements",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
local F;
F := FamilyObj( x );
return F!.set[ F!.cayleyTable[ x![ 1 ] ][ y![ 1 ] ] ];
end );
InstallOtherMethod( \*, "for a QuasigroupElement and a list",
[ IsQuasigroupElement , IsList ],
function( x, ly )
return List( ly, y -> x*y );
end );
InstallOtherMethod( \*, "for a list and a QuasigroupElement",
[ IsList, IsQuasigroupElement ],
function( lx, y )
return List( lx, x -> x*y );
end );
#############################################################################
## DIVISION
## -------------------------------------------------------------------------
## z=x/y means zy=x
InstallMethod( RightDivision, "for two quasigroup elements",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
local F, ycol;
F := FamilyObj( x );
ycol := F!.cayleyTable{ [ 1 .. F!.size ] }[ y![ 1 ] ];
return F!.set[ Position( ycol, x![ 1 ] ) ];
end );
InstallOtherMethod( RightDivision,
"for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ],
0,
function( lx, y )
return List( lx, x -> RightDivision(x, y) );
end );
InstallOtherMethod( RightDivision,
"for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ],
0,
function( x, ly )
return List( ly, y -> RightDivision(x, y) );
end );
InstallOtherMethod( \/,
"for two elements of a quasigroup",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
0,
function( x, y )
return RightDivision( x, y );
end );
InstallOtherMethod( \/,
"for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ],
0,
function( lx, y )
return List( lx, x -> RightDivision(x, y) );
end );
InstallOtherMethod( \/,
"for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ],
0,
function( x, ly )
return List( ly, y -> RightDivision(x, y) );
end );
## z = x\y means xz=y
InstallMethod( LeftDivision, "for two quasigroup elements",
IsIdenticalObj,
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
local F;
F := FamilyObj( x );
return F!.set[ Position( F!.cayleyTable[ x![ 1 ] ], y![ 1 ] ) ];
end );
InstallOtherMethod( LeftDivision,
"for a list and a quasigroup element",
[ IsList, IsQuasigroupElement ],
0,
function( lx, y )
return List( lx, x -> LeftDivision(x, y) );
end );
InstallOtherMethod( LeftDivision,
"for a quasigroup element and a list",
[ IsQuasigroupElement, IsList ],
0,
function( x, ly )
return List( ly, y -> LeftDivision(x, y) );
end );
#############################################################################
##
#O LeftDivisionCayleyTable( Q )
##
## Returns the Cayley table for the operation x\y of the quasigroup <Q>.
InstallMethod( LeftDivisionCayleyTable, "for quasigroup",
[ IsQuasigroup ],
function( Q )
# This would be slow using LeftDivision.
# Must take care of the fact that entries in ct are not necessarily 1..n
local n, ct, pos_in_Q, pos_in_parent, i, t, j;
n := Size( Q );
ct := CayleyTable( Q );
pos_in_Q := 0*[ 1..Size( Parent( Q ) ) ];
pos_in_parent := PosInParent( Q );
for i in pos_in_parent do
pos_in_Q[ i ] := Position( pos_in_parent, i );
od;
t := List( [1..n], i -> 0*[1..n] );
for i in [1..n] do for j in [1..n] do
t[ i ][ pos_in_Q[ ct[ i ][ j ] ] ] := pos_in_parent[ j ];
od; od;
return t;
end );
#############################################################################
##
#O RightDivisionCayleyTable( Q )
##
## Returns the Cayley table for the operation x/y of the quasigroup <Q>.
InstallMethod( RightDivisionCayleyTable, "for quasigroup",
[ IsQuasigroup ],
function( Q )
# This would be slow using RightDivision.
# Must take care of the fact that entries in ct are not necessarily 1..n
local n, ct, pos_in_Q, pos_in_parent, i, t, j;
n := Size( Q );
ct := CayleyTable( Q );
pos_in_Q := 0*[ 1..Size( Parent( Q ) ) ];
pos_in_parent := PosInParent( Q );
for i in pos_in_parent do
pos_in_Q[ i ] := Position( pos_in_parent, i );
od;
t := List( [1..n], i -> 0*[1..n] );
for i in [1..n] do for j in [1..n] do
t[ pos_in_Q[ ct[ i ][ j ] ] ][ j ] := pos_in_parent[ i ];
od; od;
return t;
end );
#############################################################################
## POWERS AND INVERSES
## -------------------------------------------------------------------------
InstallMethod( \^, "for a quasigroup element and a permutation",
[ IsQuasigroupElement, IsPerm ],
function( x, p )
local F;
F := FamilyObj( x );
return F!.set[ ( x![ 1 ] )^p ];
end );
InstallMethod( OneOp, "for loop elements",
[ IsLoopElement ],
function( x )
local F;
F := FamilyObj( x );
return F!.set[ 1 ];
end );
#############################################################################
##
#A LeftInverse( <x> )
##
## If <x> is a loop element, returns the left inverse of <x>
InstallMethod( LeftInverse, "for loop elements",
[ IsLoopElement ],
x -> RightDivision( One( x ), x )
);
#############################################################################
##
#A RightInverse( <x> )
##
## If <x> is a loop element, returns the left inverse of <x>
InstallMethod( RightInverse, "for loop elements",
[ IsLoopElement ],
x -> LeftDivision( x, One( x ) )
);
InstallMethod( InverseOp, "for loop elements",
[ IsLoopElement ],
function( x )
local y;
y := RightInverse( x );
if y = LeftInverse( x ) then return y; fi;
return fail;
end );
#############################################################################
## ASSOCIATORS AND COMMUTATORS
## -------------------------------------------------------------------------
#############################################################################
##
#O Associator( x, y , z )
##
## When <x>, <y>, <z> are elements of a quasigroup Q, returns the
## associator of <x>, <y>, <z>, i.e., the unique element u satisfying
## (xy)z = (x(yz))u.
InstallMethod( Associator, "for three quasigroup elements",
[ IsQuasigroupElement, IsQuasigroupElement, IsQuasigroupElement ],
function( x, y, z )
return LeftDivision( x*(y*z), (x*y)*z );
end);
#############################################################################
##
#O Commutator( x, y )
##
## When <x>, <y> are elements of a quasigroup Q, returns the
## commutator of <x>, <y>, i.e., the unique element u satisfying
## (xy) = (yx)u.
InstallMethod( Commutator, "for two quasigroup elements",
[ IsQuasigroupElement, IsQuasigroupElement ],
function( x, y )
return LeftDivision( y*x, x*y );
end);

54
gap/examples.gd Normal file
View file

@ -0,0 +1,54 @@
#############################################################################
##
#W examples.gd Examples [loops]
##
#H @(#)$Id: examples.gd, v 3.1.0 2015/09/23 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## DISPLAYING INFORMATION ABOUT A LIBRARY
## -------------------------------------------------------------------------
DeclareGlobalFunction( "DisplayLibraryInfo" );
#############################################################################
## READING LOOP FROM THE LIBRARY - GENERIC METHOD
## -------------------------------------------------------------------------
DeclareGlobalFunction( "LibraryLoop" );
# up to isomorphism
DeclareGlobalFunction( "LeftBolLoop" );
DeclareGlobalFunction( "RightBolLoop" );
DeclareGlobalFunction( "MoufangLoop" );
DeclareGlobalFunction( "PaigeLoop" );
DeclareGlobalFunction( "CodeLoop" );
DeclareGlobalFunction( "SteinerLoop" );
DeclareGlobalFunction( "CCLoop" );
DeclareGlobalFunction( "ConjugacyClosedLoop" );
DeclareGlobalFunction( "RCCLoop" );
DeclareGlobalFunction( "RightConjugacyClosedLoop" );
DeclareGlobalFunction( "LCCLoop" );
DeclareGlobalFunction( "LeftConjugacyClosedLoop" );
DeclareGlobalFunction( "SmallLoop" );
DeclareGlobalFunction( "InterestingLoop" );
DeclareGlobalFunction( "NilpotentLoop" );
DeclareGlobalFunction( "AutomorphicLoop" );
# up to isotopism
DeclareGlobalFunction( "ItpSmallLoop" );
# auxiliary
DeclareGlobalFunction( "LOOPS_LibraryByName" );
DeclareGlobalFunction( "LOOPS_SmallestNonsquare" );
DeclareGlobalFunction( "LOOPS_ActivateLeftBolLoopPQ" );
DeclareGlobalFunction( "LOOPS_ActivateLeftBolLoop" );
DeclareGlobalFunction( "LOOPS_ActivateMoufangLoop" );
DeclareGlobalFunction( "LOOPS_ActivateSteinerLoop" );
DeclareGlobalFunction( "LOOPS_ActivateRCCLoop" );
DeclareGlobalFunction( "LOOPS_ActivateCCLoop" );
DeclareGlobalFunction( "LOOPS_ActivateNilpotentLoop" );
DeclareGlobalFunction( "LOOPS_ActivateAutomorphicLoop" );

836
gap/examples.gi Normal file
View file

@ -0,0 +1,836 @@
#############################################################################
##
#W examples.gi Examples [loops]
##
#H @(#)$Id: examples.gi, v 3.3.0 2016/10/19 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## Binding global variable LOOPS_aux
##
## The variable is used for temporary storage throughout the package.
## We therefore do not want the variable to be read only.
LOOPS_aux := [];
#############################################################################
## READING DATA
## -------------------------------------------------------------------------
# up to isomorphism
ReadPackage("loops", "data/leftbol.tbl"); # left Bol loops
ReadPackage("loops", "data/moufang.tbl"); # Moufang loops
ReadPackage("loops", "data/paige.tbl"); # Paige loops
ReadPackage("loops", "data/code.tbl"); # code loops
ReadPackage("loops", "data/steiner.tbl"); # Steiner loops
ReadPackage("loops", "data/cc.tbl"); # CC loops
ReadPackage("loops", "data/rcc.tbl"); # RCC loops (more is read upon calling RCCLoop(n,m) for the first time
ReadPackage("loops", "data/small.tbl"); # small loops
ReadPackage("loops", "data/interesting.tbl"); # interesting loops
ReadPackage("loops", "data/nilpotent.tbl"); # nilpotent loops
ReadPackage("loops", "data/automorphic.tbl"); # automorphic loops
# up to isotopism
ReadPackage("loops", "data/itp_small.tbl"); # small loops up to isotopism
#############################################################################
## DISPLAYING INFORMATION ABOUT A LIBRARY
## -------------------------------------------------------------------------
#############################################################################
##
#F LOOPS_LibraryByName( name )
##
## Auxiliary. Returns the library corresponding to <name>.
InstallGlobalFunction( LOOPS_LibraryByName,
function( name )
#up to isomorphism
if name = "left Bol" then return LOOPS_left_bol_data;
elif name = "Moufang" then return LOOPS_moufang_data;
elif name = "Paige" then return LOOPS_paige_data;
elif name = "code" then return LOOPS_code_data;
elif name = "Steiner" then return LOOPS_steiner_data;
elif name = "CC" then return LOOPS_cc_data;
elif name = "RCC" then return LOOPS_rcc_data;
elif name = "small" then return LOOPS_small_data;
elif name = "interesting" then return LOOPS_interesting_data;
elif name = "nilpotent" then return LOOPS_nilpotent_data;
elif name = "automorphic" then return LOOPS_automorphic_data;
#up to isotopism
elif name = "itp small" then return LOOPS_itp_small_data;
fi;
end);
#############################################################################
##
#F DisplayLibraryInfo( name )
##
## Display information about library named <name>.
InstallGlobalFunction( DisplayLibraryInfo, function( name )
local s, lib, k;
# up to isomorphism
if name = "left Bol" then
s := "The library contains all nonassociative left Bol loops of order less than 17\nand all nonassociative left Bol loops of order p*q, where p>q>2 are primes.";
elif name = "right Bol" then
s := "The library contains all nonassociative right Bol loops of order less than 17\nand all nonassociative left Bol loops of order p*q, where p>q>2 are primes.";
name := "left Bol"; # using dual data
elif name = "Moufang" then
s := "The library contains all nonassociative Moufang loops \nof order less than 65, and all nonassociative Moufang \nloops of order 81 and 243.";
elif name = "Paige" then
s := "The library contains the smallest nonassociative finite \nsimple Moufang loop.";
elif name = "code" then
s := "The library contains all nonassociative even code loops \nof order less than 65.";
elif name = "Steiner" then
s := "The library contains all nonassociative Steiner loops \nof order less or equal to 16. It also contains the \nassociative Steiner loops of order 4 and 8.";
elif name = "CC" then
s := "The library contains all nonassociative CC loops of order less than 28 \nand all nonassociative CC loops of order p^2 and 2*p for any odd prime p.";
elif name = "RCC" then
s := "The library contains all nonassociative RCC loops of order less than 28.";
elif name = "LCC" then
s := "The library contains all nonassociative LCC loops of order less than 28.";
name := "RCC"; # using dual data
elif name = "small" then
s := "The library contains all nonassociative loops of order less than 7.";
elif name = "interesting" then
s := "The library contains a few interesting loops.";
elif name = "nilpotent" then
s := "The library contains all nonassociative nilpotent loops \nof order less than 12.";
elif name = "automorphic" then
s := "The library contains:\n";
s := Concatenation(s," - all nonassociative automorphic loops of order less than 16,\n");
s := Concatenation(s," - all commutative automorphic loops of order 3, 9, 27, 81,\n");
s := Concatenation(s," - all commutative automorphic loops of order 243 that are central\n");
s := Concatenation(s," extensions of Z_3 by F, where F is not the elem. ab. 3-group.\n");
s := Concatenation(s,"Note: Abelian groups are included among the commutative loops.");
# up to isotopism
elif name = "itp small" then
s := "The library contains all nonassociative loops of order less than 7 up to isotopism.";
else
Info( InfoWarning, 1, Concatenation(
"The admissible names for loop libraries are: \n",
"[ \"left Bol\", \"right Bol\", \"Moufang\", \"Paige\", \"code\", \"Steiner\", \"CC\", \"RCC\", \"LCC\", \"small\", \"itp small\", \"interesting\", \"nilpotent\", \"automorphic\" ]."
) );
return fail;
fi;
s := Concatenation( s, "\n------\nExtent of the library:" );
lib := LOOPS_LibraryByName( name );
for k in [1..Length( lib[ 1 ] ) ] do
if lib[ 2 ][ k ] = 1 then
s := Concatenation( s, "\n ", String( lib[ 2 ][ k ] ), " loop of order ", String( lib[ 1 ][ k ] ) );
else
s := Concatenation( s, "\n ", String( lib[ 2 ][ k ] ), " loops of order ", String( lib[ 1 ][ k ] ) );
fi;
od;
if name = "left Bol" or name = "right Bol" then
s := Concatenation( s, "\n (p-q)/2 loops of order p*q for primes p>q>2 such that q divides p-1");
s := Concatenation( s, "\n (p-q+2)/2 loops of order p*q for primes p>q>2 such that q divides p+1" );
fi;
if name = "CC" then
s := Concatenation( s, "\n 3 loops of order p^2 for every odd prime p,\n 1 loop of order 2*p for every odd prime p" );
fi;
s := Concatenation( s, "\n" );
Print( s );
return true;
end);
#############################################################################
## AUXILIARY FUNCTIONS
## -------------------------------------------------------------------------
## When the data in the database is encoded in some way, we usually
## retrieve the loop via function LOOPS_ActivateXLoop, where X is the name
## of the class.
#############################################################################
##
#F LOOPS_SmallestNonsquare( p )
##
## Auxiliary function.
## Returns the smallest nonsquare modulo p.
InstallGlobalFunction( LOOPS_SmallestNonsquare,
function( p )
local squares, i;
squares := Set( [1..p-1], i->i^2 mod p );
for i in [2..p-1] do
if not i in squares then return i; fi;
od;
return fail; # will never happen if p>2 is prime
end);
#############################################################################
##
#F LOOPS_ActivateLeftBolLoopPQ( p, q, m )
##
## Auxiliary function for activating left Bol loop of order p*q.
## See paper by Kinyon, Nagy and Vojtechovsky.
## p>q>2 are primes such that q divides p^2-1, m is an integer in the range [1..(p-q)/2] or [1..(p-q+2)/2]
InstallGlobalFunction( LOOPS_ActivateLeftBolLoopPQ,
function( p, q, m )
local F, omega, lambda, ev, inv_ev, params, t, sqrt_t, final_params, alpha, theta, GFp, ct, i, j, k, l, u, v, w, x, y;
F := GF(p^2);
omega := PrimitiveRoot( F )^((p^2-1)/q);
lambda := omega + omega^(-1);
ev := List([0..q-1], j -> omega^j);
inv_ev := List([0..q-1], j -> omega^(-j));
if IsInt((p-1)/q) then
params := List([2..p-1], j->j*One(F)); # GF(p); 0 and 1 correspond to isomorphic nonabelian groups
params := Filtered( params, x -> not ((One(F) - x^(-1)) in ev) );
else # q divides p+1
t := LOOPS_SmallestNonsquare( p );
sqrt_t := RootsOfPolynomial( F, X(F,"x")^2 - t )[ 1 ]; # a squre root of t in GF(p^2)
params := List([0..p-1], j -> (1/2)*One(F) + j*One(F)*sqrt_t ); # 1/2 + GF(p)\sqrt{t}
params := Filtered( params, x -> not ((One(F) - x^(-1)) in ev) );
fi;
final_params := [];
for x in params do
if not ( (One(F)-x) in final_params ) then
Add( final_params, x );
fi;
od;
alpha := final_params[ m ];
theta := alpha*ev + (One(F)-alpha)*inv_ev;
theta := List( theta, x -> x^(-1) );
GFp := List([0..p-1], j -> j*One(F));
theta := List( theta, x -> Position( GFp, x )-1 );
# construct the Cayley table according to (a^i b^j)*(a^k b^l) = a^{i+k} b^{ w + (l+w)*theta[k]^{-1}*theta[i+k], where w+w*theta[i] = j
ct := List([1..p*q], i -> 0*[1..p*q]);
for i in [0..q-1] do for j in [0..p-1] do for k in [0..q-1] do for l in [0..p-1] do
u := (i+k) mod q;
w := ( j/(1+theta[i+1]) ) mod p;
v := ( w + (l+w)*theta[ ((i+k) mod q) + 1 ]/theta[k+1] ) mod p;
x := i*p+j+1;
y := k*p+l+1;
ct[x][y] := u*p+v+1;
od; od; od; od;
# return the loop
return LoopByCayleyTable( ct );
end);
#############################################################################
##
#F LOOPS_ActivateLeftBolLoop( pos_n, m, case )
##
## Auxiliary function for activating left Bol loops from the database.
## case = [p,q] if it is a left Bol loop of order p*q, with p>q>2 primes such that q divides p^2-1
## case = false otherwise
## pos_n is meaningless when case = [p,q]
InstallGlobalFunction( LOOPS_ActivateLeftBolLoop,
function( pos_n, m, case )
local rep_m, ct, perm;
if not case=false then # p*q
return LOOPS_ActivateLeftBolLoopPQ( case[1], case[2], m );
fi;
# in database
rep_m := m;
# searching for a Cayley table on which the loop is based
while not IsString( LOOPS_left_bol_data[ 3 ][ pos_n ][ rep_m ] ) do
rep_m := rep_m - 1;
od;
if rep_m = m then # loop given by encoded Cayley table
ct := LOOPS_DecodeCayleyTable( LOOPS_left_bol_data[ 3 ][ pos_n ][ m ] );
else # loop given as an isotope of another loop
ct := LOOPS_DecodeCayleyTable( LOOPS_left_bol_data[ 3 ][ pos_n ][ rep_m ] );
perm := PermList( ct[ LOOPS_left_bol_data[ 3 ][ pos_n ][ m ] ] );
ct := Set( List( ct, row -> OnTuples( row, perm^-1 ) ) );
fi;
return LoopByCayleyTable( ct );
end);
#############################################################################
##
#F LOOPS_ActivateMoufangLoop( n, pos_n, m )
##
## Auxiliary function for activating Moufang loops from the database.
InstallGlobalFunction( LOOPS_ActivateMoufangLoop,
function( n, pos_n, m )
local d, parent_m, parent, S, a, h, e, f, G, ret, x, row, y, b, c, z;
d := LOOPS_moufang_data[ 3 ][ pos_n ][ m ]; #data
# orders 81 and 243 are represented as central extensions
if n = 81 or n = 243 then
return LoopByExtension(
IntoLoop( SmallGroup( d[1], d[2] ) ), # K
IntoLoop( SmallGroup( d[3], d[4] ) ), # F
List([1..d[3]], i -> () ), # trivial action F -> Aut( K )
LOOPS_DecodeCocycle( [ n/d[1], false, d[5] ], [1..d[1]] ) # cocycle
);
fi;
# all other orders
if d[ 1 ] > 0 then # must activate parent first
# determine position of parent ( d[1] gives it relative to the class leader )
while LOOPS_moufang_data[ 3 ][ pos_n ][ m ][ 1 ] > 0 do
m := m - 1;
od;
m := m + d[ 1 ] - 1;
parent := LOOPS_ActivateMoufangLoop( n, pos_n, m );
if d[ 2 ] = "C" then #cyclic modification
S := List( d[ 3 ], i -> Elements( parent )[ i ] );
a := Elements( parent )[ d[ 4 ] ];
h := Elements( parent )[ d[ 5 ] ];
return LoopByCyclicModification( parent, S, a, h );
fi;
if d[ 2 ] = "D" then # dihedral modification
S := List( d[ 3 ], i -> Elements( parent )[ i ] );
e := Elements( parent )[ d[ 4 ] ];
f := Elements( parent )[ d[ 5 ] ];
h := Elements( parent )[ d[ 6 ] ];
return LoopByDihedralModification( parent, S, e, f, h );
fi;
fi;
# no parent;
if d[ 2 ]="G" then # loop of type MG2
G := AllGroups( d[ 3 ], IsCommutative, false)[ d[ 4 ] ]; #relies on GAP group libraries !!
return LoopMG2( G );
fi;
if d[ 2 ]="T" then # special loop (direct product of an old loop and a cyclic group)
parent := MoufangLoop( d[ 3 ][ 1 ], d[ 3 ][ 2 ]);
return DirectProduct( parent, CyclicGroup( d[ 3 ][ 3 ] ) );
fi;
end);
#############################################################################
##
#F LOOPS_ActivateSteinerLoop( n, pos_n, m )
##
## Auxiliary function activating Steiner loops from the database.
##
## The database LOOPS_steiner_data contains blocks of steiner triple systems.
## If the system is on k points, the poitns are labelled 0,...,k-1.
## The constructed Steiner loop has elements labelled 1,...,k+1=n
InstallGlobalFunction( LOOPS_ActivateSteinerLoop,
function( n, pos_n, m )
local d, blocks, i, T, i_in, ij_in, j, MyInt;
#############################################################################
##
#F MyInt( s )
##
## Auxiliary function.
## Given a digit or a lower case letter, returns the numerical value, where
## a = 10, f=15
MyInt := function( s )
return Position( "0123456789abcdef", s ) - 1;
end;
d := LOOPS_steiner_data[ 3 ][ pos_n ][ m ]; # data for the loop = three strings
# creating the blocks
blocks := [];
for i in [1..Length( d[ 1 ] )] do
Add( blocks, [ MyInt( d[1][i] ), MyInt( d[2][i] ), MyInt( d[3][i] ) ] );
od;
#creating the multiplication table
T := List( [1..n], i->[1..n] );
for i in [1..n] do T[i][1] := i; od;
for i in [0..n-2] do
i_in := Filtered( blocks, B->i in B);
for j in [0..n-2] do
if j=i then T[i+2][j+2] := 1; fi;
if not j=i then
ij_in := Filtered( i_in, B->j in B )[1]; #unique block;
T[i+2][j+2] := Difference( ij_in, [i,j])[1] + 2;
fi;
od;
od;
return LoopByCayleyTable( T );
end);
#############################################################################
##
#F LOOPS_ActivateRCCLoop( n, pos_n, m )
##
## Activates an RCC loop from the library.
## See manual for complete discussion concerning this library.
InstallGlobalFunction( LOOPS_ActivateRCCLoop,
function( n, pos_n, m )
local pos_m, g, nr_conj_classes, data, data2, next_compactified, x, i, rel_m, G, section, pos_conjugacy_classes;
if IsEmpty( LOOPS_rcc_transitive_groups ) then # data not read yet
ReadPackage( "loops", "data/rcc/rcc_transitive_groups.tbl" );
fi;
# determining the transitive group corresponding to pos_n, m
pos_m := Length( LOOPS_rcc_data[ 3 ][ pos_n ][ 1 ] ); # nr of transitive groups associated with order n
while LOOPS_rcc_data[ 3 ][ pos_n ][ 2 ][ pos_m ] > m do
pos_m := pos_m - 1;
od;
g := LOOPS_rcc_data[ 3 ][ pos_n ][ 1 ][ pos_m ]; # index of transitive group (of degree n) in GAP library
# activating data for the group, if needed
if not IsBound( LOOPS_rcc_sections[ pos_n ][ pos_m ] ) then
# data must be read from file and decoded
ReadPackage( "loops", Concatenation( "data/rcc/sections", String(n), ".", String(g), ".tbl" ) );
# variable LOOPS_aux is now read and ready to be processed
nr_conj_classes := Length( LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ] );
data := SplitString( LOOPS_aux, " " );
data2 := [];
next_compactified := false;
for x in data do
if x="" then # the next entry is compactified, eg. "a$X" means "a", "$", "X"
next_compactified := true;
elif not next_compactified then
Add( data2, x );
else # compactified string
next_compactified := false;
for i in [1..Length(x)] do
Add( data2, x{[i..i]} ); # Add( data2, x[i] ) is not safe when data2 is empty
od;
fi;
od;
data := List( data2, x -> LOOPS_ConvertToDecimal( x, 91 ) );
# reconstructing the sequence from the difference sequence
for i in [2..Length( data )] do
data[ i ] := data[ i-1 ] - data[ i ];
od;
LOOPS_rcc_sections[ pos_n ][ pos_m ] := data;
fi;
# data is now loaded
G := TransitiveGroup(n, g);
rel_m := m - LOOPS_rcc_data[ 3 ][ pos_n ][ 2 ][ pos_m ] + 1; # relative position of the loop in the file for G
section := [];
nr_conj_classes := Length( LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ] );
if not LOOPS_rcc_conjugacy_classes[ 1 ] = [ n, g ] then # must calculate conjugacy classes, so let's reset old data
LOOPS_rcc_conjugacy_classes[ 1 ] := [ n, g ];
LOOPS_rcc_conjugacy_classes[ 2 ] := List( [1..nr_conj_classes], x->[] );
fi;
x := LOOPS_rcc_sections[ pos_n ][ pos_m ][ rel_m ];
x := LOOPS_ConvertFromDecimal( x, 2, nr_conj_classes ); # convert to a binary string of prescribed length
pos_conjugacy_classes := Positions( x, '1' );
for i in [1..nr_conj_classes] do
if LOOPS_rcc_conjugacy_classes[ 2 ][ i ] = [] then
LOOPS_rcc_conjugacy_classes[ 2 ][ i ] := Elements( ConjugacyClass( G, LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ][ i ] ) );
fi;
od;
section := Concatenation( LOOPS_rcc_conjugacy_classes[ 2 ]{pos_conjugacy_classes} );
Add( section, One( G ) ); # the trivial class is contained in all loops and never stored
Sort( section );
return LoopByRightSection( section );
end);
#############################################################################
##
#F LOOPS_ActivateCCLoop( n, pos_n, m, case )
##
## Activates a CC-loop from the library.
## The argument p_case is set to [p,"p^2"] if n = p^2, to [p,"2*p"] if n=2*p, and false otherwise.
## See manual for complete discussion concerning this library.
InstallGlobalFunction( LOOPS_ActivateCCLoop,
function( n, pos_n, m, case )
local T, x, y, k, a, b, p;
if case=false then # use library of RCC loops, must recalculate pos_n
return LOOPS_ActivateRCCLoop( n, Position(LOOPS_rcc_data[ 1 ], n), LOOPS_cc_data[ 3 ][ pos_n ][ m ] );
fi;
# parameters n, m are already checked to be permissible
p := case[ 1 ];
if case[ 2 ] = "2*p" then # 2*p
T := List( [1..n], i -> [1..n ] );
for a in [0..p-1] do for b in [0..p-1] do
T[a+1][b+1]:= ((a+b) mod p) + 1;
T[a+1][p+b+1] := p + ((-a+b) mod p ) + 1;
T[p+a+1][b+1] := p + ((a+b) mod p) + 1;
T[p+a+1][p+b+1] := ((1-a+b) mod p) + 1;
od; od;
return LoopByCayleyTable( T );
fi;
# p^2
T := List([1..n], i->[1..n]);
if m = 1 then
for x in [0..n-1] do for y in [0..n-1] do
T[ x+1 ][ y+1 ] := ((x + y + p*(x^2)*y) mod n) + 1;
od; od;
elif m = 2 then
k := LOOPS_SmallestNonsquare( p );
for x in [0..n-1] do for y in [0..n-1] do
T[ x+1 ][ y+1 ] := ((x + y + k*p*(x^2)*y) mod n) + 1;
od; od;
elif m = 3 then
for x in [0..p-1] do for y in [0..p-1] do for a in [0..p-1] do for b in [0..p-1] do
T[ x*p+a+1 ][ y*p+b+1 ] := ((x+y) mod p)*p + ((a+b+(x^2)*y) mod p) + 1;
od; od; od; od;
fi;
return LoopByCayleyTable( T );
end);
#############################################################################
##
#F LOOPS_ActivateNilpotentLoop( data )
##
## Activates the nilpotent loop based on data = [ K, F, t ], where
## K determines a central (normal) subloop,
## F determines the factor loop,
## t determines the cocycle.
## Understanding K and F:
## If the value of K or F is in [2,3,4,5], it is the cyclic group of order V.
## If the value of K or F is 0, it is the Klein group.
## Understanding t:
## The cocycle is mapping from F x F to K. Let f = |F|. Let k = |K|.
## The value t corresponds to a (f-1)x(f-1) array of values in [0..k-1].
## It is represented by a single integer in base k, with the least
## significant digit in the first row and first column, then following
## the rows.
## Once t is decoded into a (f-1)x(f-1) array, 1 is added to all entries.
## Then a first row and forst column of all ones is added to t,
## resulting in a f x f array.
## The loop is then obtained via LoopByExtension( K, F, phi, t), where
## phi is trivial.
InstallGlobalFunction( LOOPS_ActivateNilpotentLoop,
function( data )
local K, F, f, k, t, theta, i, j, phi;
# preparing normal subloop and factor loop
if data[ 1 ] = 0 then
K := IntoLoop( Group( (1,2),(3,4) ) );
else
K := IntoLoop( CyclicGroup( data[ 1 ] ) );
fi;
if data[ 2 ] = 0 then
F := IntoLoop( Group( (1,2),(3,4) ) );
else
F := IntoLoop( CyclicGroup( data[ 2 ] ) );
fi;
# preparing cocycle
f := Size( F );
k := Size( K );
t := data[ 3 ];
theta := List( [1..f], i->[1..f] );
for i in [2..f] do
theta[ 1 ][ i ] := 1;
od;
for i in [2..f] do for j in [2..f] do
theta[ i ][ j ] := t mod k;
t := (t - theta[i][j])/k;
theta[ i ][ j ] := theta[ i ][ j ] + 1;
od; od;
# preparing trivial action
phi := List([1..f], i -> () );
# constructing the loop
return LoopByExtension( K, F, phi, theta );
end);
#############################################################################
##
#F LOOPS_ActivateAutomorphicLoop( n, m )
##
## Activates an automorphic loop from the library.
InstallGlobalFunction( LOOPS_ActivateAutomorphicLoop,
function( n, m )
local i, pos_n, factor_id, F, dim, coords, basis, coc;
if IsEmpty( LOOPS_automorphic_cocycles ) then # only read on demand
ReadPackage( "loops", "data/automorphic/automorphic_cocycles.tbl");
# decode cocycles
for i in [1..3] do
LOOPS_automorphic_cocycles[ i ] := List( LOOPS_automorphic_cocycles[ i ],
c -> LOOPS_DecodeCocycle( [ 3^(i+2), true, c ], [0,1,2] )
);
od;
# separate coordinates (from a long string )
for i in [1..3] do
LOOPS_automorphic_coordinates[ i ] := SplitString( LOOPS_automorphic_coordinates[ i ], " " );
od;
fi;
# factor loop
pos_n := Position( [27,81,243], n );
factor_id := LOOPS_CharToDigit( LOOPS_automorphic_coordinates[ pos_n ][ m ][ 1 ] );
F := AutomorphicLoop( n/3, factor_id );
# coordinates determining the cocycle
dim := Length( LOOPS_automorphic_bases[ pos_n ][ factor_id ] );
coords := LOOPS_automorphic_coordinates[ pos_n ][ m ];
coords := coords{[2..Length(coords)]}; # remove the character that determines factor id
coords := LOOPS_ConvertBase( coords, 91, 3, dim );
coords := List( coords, LOOPS_CharToDigit );
# basis
basis := List( LOOPS_automorphic_bases[ pos_n ][ factor_id ],
i -> LOOPS_automorphic_cocycles[ pos_n ][ i ]
);
# calculate cocycle
coc := (coords*basis) mod 3;
coc := List( coc, i -> i+1 );
# return extension of Z_3 by F using cocycle and trivial action
return LoopByExtension( AutomorphicLoop(3,1), F, List([1..n/3], i -> () ), coc );
end);
#############################################################################
## READING LOOPS FROM THE LIBRARY - GENERIC METHOD
## -------------------------------------------------------------------------
#############################################################################
##
#F LibraryLoop( name, n, m )
##
## returns he <m>th loop of order <n> from the library named <name>
InstallGlobalFunction( LibraryLoop, function( name, n, m )
local lib, implemented_orders, NOL, loop, pos_n, p, q, divs, PG, m_inv, root, half, case, g, h;
# selecting data library
if name = "right Bol" then # using dual data
lib := LOOPS_LibraryByName( "left Bol" );
elif name = "LCC" then # using dual data
lib := LOOPS_LibraryByName( "RCC" );
else
lib := LOOPS_LibraryByName( name );
fi;
# extent of the library
implemented_orders := lib[ 1 ];
# number of loops of given order in the library
NOL := lib[ 2 ];
# testing arguments
if (not n in Integers) or (not m in Integers) or not (n>0) or not (m>0) then
Error("LOOPS: Both arguments must be positive integers.");
fi;
# parameters for handling systematic cases, such as CCLoop( p^2, 1 )
pos_n := fail;
case := false;
if name="left Bol" or name="right Bol" then
divs := DivisorsInt( n );
if Length( divs ) = 4 and not IsInt( divs[3]/divs[2] ) then # case n = p*q
q := divs[ 2 ];
p := divs[ 3 ];
case := [p,q];
if not (IsOddInt( q ) and IsInt((p^2-1)/q)) then
Error("LOOPS: Nonassociative ", name, " loops of order p*q exist only for primes p>q>2 such that q divides p^2-1.");
fi;
if IsInt((p-1)/q) and (not m in [1..(p-q)/2]) then
Error("LOOPS: There are only ", (p-q)/2, " nonassociative ", name, " loops of order ", n, ".");
fi;
if IsInt((p+1)/q) and (not m in [1..(p-q+2)/2]) then
Error("LOOPS: There are only ", (p-q+2)/2, " nonassociative ", name, " loops of order ", n, ".");
fi;
fi;
fi;
if name="CC" then
divs := DivisorsInt( n );
if Length( divs ) = 3 then # case p^2
p := divs[ 2 ];
case := [p,"p^2"];
if not m in [1..3] then
Error("LOOPS: There are only 3 nonassociative CC-loops of order p^2 for an odd prime p.");
fi;
elif Length( divs ) = 4 and not IsInt( divs[3]/divs[2] ) then # p*q
p := divs[ 3 ];
case := [p,"2*p"];
if not divs[2] = 2 then
Error("LOOPS: Order ", n, " not implemented.");
fi;
if not m=1 then
Error("LOOPS: There is only 1 nonassociative CC-loop of order 2*p for an odd prime p.");
fi;
fi;
fi;
if case=false then
if not n in implemented_orders then
Error("LOOPS: Order ", n, " not implemented.");
fi;
pos_n := Position( implemented_orders, n );
if NOL[ pos_n ] < m then
if NOL[ pos_n ] = 1 then
Error("LOOPS: There is only ", NOL[ pos_n ], " ", name, " loop of order ", n, " in the library.");
else
Error("LOOPS: There are only ", NOL[ pos_n ], " ", name, " loops of order ", n, " in the library.");
fi;
fi;
fi;
# activating the desired loop (treat cases separately below)
# up to isomorphism
if name = "left Bol" then
loop := LOOPS_ActivateLeftBolLoop( pos_n, m, case );
SetIsLeftBolLoop( loop, true );
elif name = "right Bol" then
loop := OppositeLoop( LOOPS_ActivateLeftBolLoop( pos_n, m, case ) );
SetIsRightBolLoop( loop, true );
elif name = "Moufang" then
# renaming loops so that they agree with Goodaire's classification
PG := List([1..243], i->());
PG[16] := (2,5,3,4);
PG[24] := (1,3,4)(2,5);
PG[32] := (1,4,61,7,64,31,19,44,3,51,24,46,22,49,65,9,50,39,41,2,63,27,57,14,5,55,13,69,32,58,17,53,15)
(6,71,28,36,30,47,18,42,26,60,21,59,35,52,25,40,23,43,29,56,20,66,8,70,33,67,38,54,16,62,37,11,68,34,45,10,48,12);
PG[36] := (1,2);
PG[40] := (1,2,5,3,4);
PG[48] := (1,11,43,49,30,7,28,23,36,21,50,31,41,32,46,51,29,12,42,15)
(2,25,24,35,22,8,39,5,6,38,16,19,17,37,34,33,4,47,13,20,10,40,48,14,3,26,45)
(9,27,44);
PG[56] := (1,2,4);
PG[60] := (1,2);
m_inv := m^Inverse( PG[ n ] );
# activating the loop
loop := LOOPS_ActivateMoufangLoop( n, pos_n, m_inv );
SetIsMoufangLoop( loop, true );
elif name = "Paige" then
loop := LoopByCayleyTable( lib[ 3 ][ 1 ][ 1 ] ); #only one loop there at this point
SetIsMoufangLoop( loop, true );
elif name = "code" then
loop := LibraryLoop( "Moufang", n, lib[ 3 ][ pos_n ][ m ] );
SetIsCodeLoop( loop, true );
elif name = "Steiner" then
loop := LOOPS_ActivateSteinerLoop( n, pos_n, m );
SetIsSteinerLoop( loop, true );
elif name = "CC" then
loop := LOOPS_ActivateCCLoop( n, pos_n, m, case );
SetIsCCLoop( loop, true );
elif name = "RCC" then
loop := LOOPS_ActivateRCCLoop( n, pos_n, m );
SetIsRCCLoop( loop, true );
elif name = "LCC" then
loop := OppositeLoop( LOOPS_ActivateRCCLoop( n, pos_n, m ) );
SetIsLCCLoop( loop, true );
elif name = "small" then
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ] ) );
elif name = "interesting" then
if [n,m] = [96,1] then # simple Bol loop of order 96
g := Group((1,4)(2,9)(3,10)(6,11)(7,12)(13,21)(14,22)(15,24)(16,23)(17,30)(18,29)(19,31)(20,32)(33,35)(38,40),
(1,2,4,6,8,7,5,3)(9,13,25,18,10,14,26,17)(11,15,27,20,12,16,28,19)(21,30,38,34,23,31,40,35)(22,32,39,36,24,29,37,33));
h := Normalizer( g, SylowSubgroup( g, 5) );
g := Action( g, RightCosets( g, h ), OnRight );
loop := LoopByRightSection(Union(Filtered(ConjugacyClasses(g),c->Size(c) in [1,15,80])));
else
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ][ 1 ] ) );
fi;
SetName( loop, lib[ 3 ][ pos_n ][ m ][ 2 ] );
elif name = "nilpotent" then
loop := LOOPS_ActivateNilpotentLoop( lib[ 3 ][ pos_n ][ m ] );
elif name = "automorphic" then
if not n in [27,81,243] then # use Cayley table
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ] ) );
else # use cocycles
loop := LOOPS_ActivateAutomorphicLoop( n, m );
fi;
SetIsAutomorphicLoop( loop, true );
# up to isotopism
elif name = "itp small" then
return LibraryLoop( "small", n, lib[ 3 ][ pos_n ][ m ] );
fi;
# setting the name
if not name = "interesting" then
SetName( loop, Concatenation( "<", name, " loop ", String( n ), "/", String( m ), ">" ) );
fi;
# returning the loop
return loop;
end);
#############################################################################
## READING LOOPS FROM THE LIBRARY - SPECIFIC CALLS
## -------------------------------------------------------------------------
#############################################################################
##
#F LeftBolLoop( n, m )
#F RightBolLoop( n, m )
#F MoufangLoop( n, m )
#F PaigeLoop( q )
#F CodeLoop( n, m )
#F SteinerLoop( n, m )
#F CCLoop( n, m )
#F SmallLoop( n, m )
#F InterestingLoop( n, m )
#F NilpotentLoop( n, m )
#F AutomorphicLoop( n, m )
#F ItpSmallLoop( n, m )
##
InstallGlobalFunction( LeftBolLoop, function( n, m )
return LibraryLoop( "left Bol", n, m );
end);
InstallGlobalFunction( RightBolLoop, function( n, m )
return LibraryLoop( "right Bol", n, m );
end);
InstallGlobalFunction( MoufangLoop, function( n, m )
return LibraryLoop( "Moufang", n, m );
end);
InstallGlobalFunction( PaigeLoop, function( q )
# Paige loop over GF(q)
if not q=2 then return Error("LOOPS: Only q=2 is implemented."); fi;
return LibraryLoop( "Paige", 120, 1 );
end);
InstallGlobalFunction( CodeLoop, function( n, m )
return LibraryLoop( "code", n, m );
end);
InstallGlobalFunction( SteinerLoop, function( n, m )
return LibraryLoop( "Steiner", n, m );
end);
InstallGlobalFunction( CCLoop, function( n, m )
return LibraryLoop( "CC", n, m );
end);
InstallGlobalFunction( ConjugacyClosedLoop, function( n, m )
return LibraryLoop( "CC", n, m );
end);
InstallGlobalFunction( RCCLoop, function( n, m )
return LibraryLoop( "RCC", n, m );
end);
InstallGlobalFunction( RightConjugacyClosedLoop, function( n, m )
return LibraryLoop( "RCC", n, m );
end);
InstallGlobalFunction( LCCLoop, function( n, m )
return LibraryLoop( "LCC", n, m );
end);
InstallGlobalFunction( LeftConjugacyClosedLoop, function( n, m )
return LibraryLoop( "LCC", n, m );
end);
InstallGlobalFunction( SmallLoop, function( n, m )
return LibraryLoop( "small", n, m );
end);
InstallGlobalFunction( InterestingLoop, function( n, m )
return LibraryLoop( "interesting", n, m );
end);
InstallGlobalFunction( NilpotentLoop, function( n, m )
return LibraryLoop( "nilpotent", n, m );
end);
InstallGlobalFunction( AutomorphicLoop, function( n, m )
return LibraryLoop( "automorphic", n, m );
end);
InstallGlobalFunction( ItpSmallLoop, function( n, m )
return LibraryLoop( "itp small", n, m );
end);

14
gap/extensions.gd Normal file
View file

@ -0,0 +1,14 @@
############################################################################
##
#W extensions.gd Extensions [loops]
##
#H @(#)$Id: extensions.gd, v 2.0.0 2008/01/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareOperation( "LoopByExtension", [ IsLoop, IsLoop, IsList, IsMatrix ] );
DeclareOperation( "NuclearExtension", [ IsLoop, IsLoop ] );

79
gap/extensions.gi Normal file
View file

@ -0,0 +1,79 @@
#############################################################################
##
#W extensions.gi Extensions [loops]
##
#H @(#)$Id: extensions.gi, v 3.0.0 2015/06/15 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#O LoopByExtension( K, F, phi, theta )
##
## Let K be an abelian group, F a loop, phi: F --> Aut( K ) a homomorphism,
## and theta: F x F --> K a map (cocycle) satisfying theta(1,x) = theta(x,1)
## = 1. Then the function returns the extension K x F with multiplication *
## defined by (a,x)*(b,y) = (a phi(x)(b) theta(x,y), xy).
##
## Note about arguments:
## phi is a list of length |F| of permutations of [1, ..., K ],
## theta is am |F| by |F| matrix of numbers from [1, ..., K].
InstallMethod( LoopByExtension, "for a commutative associative loop and a loop",
[ IsLoop, IsLoop, IsList, IsMatrix ],
function( K, F, phi, theta )
local nK, nF, ctK, ctF, ct, a, b, x, y, c, z;
# only some properties of the arguments are checked
if not IsAssociative( K ) then Error("LOOPS: <1> must be an associative loop."); fi;
if not IsCommutative( K ) then Error("LOOPS: <1> must be a commutative loop."); fi;
# sizes
nK := Size( K );
nF := Size( F );
# making sure all is canonical
ctK := CanonicalCayleyTable( CayleyTable( K ) );
ctF := CanonicalCayleyTable( CayleyTable( F ) );
# future Cayley table
ct := List([1..nK*nF], i -> 0 * [1..nK*nF] );
# constructing the Cayley table
for a in [1..nK] do for b in [1..nK] do for x in [1..nF] do for y in [1..nF] do
c := ctK[ a ][ b^phi[x] ];
c := ctK[ c ][ theta[ x ][ y ] ];
z := ctF[ x ][ y ];
ct[ nK*(x-1) + a ][ nK*(y-1) + b ] := nK*(z-1) + c;
od; od; od; od;
return LoopByCayleyTable( ct );
end);
#############################################################################
##
#O NuclearExtension( Q, K )
##
## Let Q be a loop and K an abelian normal subloop of Q contained in the
## nucleus of Q. Then Q is an extension of K by Q/K = F via a map
## phi: F --> Aut( K ) and a cocycle theta: F x F --> K, as above.
## This function returns the for ingredients [ K, F, phi, theta ],
## all in a canonical form.
InstallMethod( NuclearExtension, "for a loop and a normal nuclear subloop",
[ IsLoop, IsLoop ],
function( Q, K )
local F, t, phi, theta, i, j, elmK;
# checking arguments
if not IsNormal( Q, K ) then Error("LOOPS: <2> must be a normal subloop of <1>."); fi;
if not IsCommutative( K ) then Error("LOOPS: <2> must be a commutative loop."); fi;
if not ForAll( K, k -> k in Nuc( Q ) ) then Error("LOOPS: <2> must be contained in the nucleus of <1>."); fi;
# partitioning Q nicely into cosets of K (not needed but it aids in visualization)
Q := IsomorphicCopyByNormalSubloop( Q, K );
K := Subloop( Q, [1..Size(K)] );
F := FactorLoop( Q, K );
t := List( [1..Size(F)], i -> Elements(Q)[(i-1)*Size(K) + 1] ); #transversal
phi := List( t, x -> RestrictedPerm( MiddleInnerMapping( Q, x ), [1..Size(K)] ) ); #action
theta := List( [1..Size(F)], i -> [1..Size(F)] ); #cocycle
for i in [1..Size(F)] do for j in [1..Size(F)] do
elmK := RightDivision( t[i]*t[j], t[ CayleyTable( F )[ i ][ j ] ] );
theta[ i ][ j ] := Position( Elements( K ), elmK );
od; od;
return [ K, F, phi, theta ];
end);

52
gap/iso.gd Normal file
View file

@ -0,0 +1,52 @@
#############################################################################
##
#W iso.gd Isomorphisms and isotopisms [loops]
##
#H @(#)$Id: iso.gd, v 3.2.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## DISCRIMINATOR
## -------------------------------------------------------------------------
DeclareOperation( "Discriminator", [ IsQuasigroup ] );
DeclareOperation( "AreEqualDiscriminators", [ IsList, IsList ] );
#############################################################################
## ISOMORPHISMS OF QUASIGROUPS AND LOOPS
## -------------------------------------------------------------------------
DeclareOperation( "IsomorphismQuasigroups", [ IsQuasigroup, IsQuasigroup ] );
DeclareOperation( "IsomorphismLoops", [ IsLoop, IsLoop ] );
DeclareOperation( "QuasigroupsUpToIsomorphism", [ IsList ] );
DeclareOperation( "LoopsUpToIsomorphism", [ IsList ] );
DeclareOperation( "IsomorphicCopyByPerm", [ IsQuasigroup, IsPerm ] );
DeclareOperation( "IsomorphicCopyByNormalSubloop", [ IsLoop, IsLoop ] );
#############################################################################
## AUTOMORPHISMS AND AUTOMORPHISM GROUPS
## -------------------------------------------------------------------------
# AutomorphismGroup already declared for groups
#############################################################################
## ISOTOPISM OF LOOPS
## ------------------------------------------------------------------------
DeclareOperation( "IsotopismLoops", [ IsLoop, IsLoop ] );
DeclareOperation( "LoopsUpToIsotopism", [ IsList ] );
#############################################################################
## AUXILIARY
## -------------------------------------------------------------------------
DeclareGlobalFunction( "IsomorphismQuasigroupsNC" );
DeclareGlobalFunction( "LOOPS_EfficientGenerators" );
DeclareGlobalFunction( "LOOPS_ExtendHomomorphismByClosingSource" );
DeclareGlobalFunction( "LOOPS_ExtendIsomorphism" );
DeclareGlobalFunction( "LOOPS_SublistPosition" );
DeclareGlobalFunction( "LOOPS_AutomorphismsFixingSet" );

690
gap/iso.gi Normal file
View file

@ -0,0 +1,690 @@
#############################################################################
##
#W iso.gi Isomorphisms and isotopisms [loops]
##
#H @(#)$Id: iso.gi, v 3.3.0 2016/10/26 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## DISCRIMINATOR
## -------------------------------------------------------------------------
#############################################################################
##
#O Discriminator( Q )
##
## Returns the dicriminator of a quasigroup <Q>.
## It is a list [ A, B ], where A is a list of the form
## [ [I1,n1], [I2,n2],.. ], where the invariant Ii occurs ni times in Q,
## and where B[i] is a subset of [1..Size(Q)] corresponding to elements of
## Q with invariant Ii.
##
## PROG: Invariants 8, 9 can be slow for large quasigroups.
InstallMethod( Discriminator, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local n, T, I, case, i, j, k, ebo, js, ks, count1, count2, A, P, B, perm, p;
# making sure the quasigroup is canonical
if not Q = Parent( Q ) then Q := CanonicalCopy( Q ); fi;
n := Size( Q );
# converting a non-declared loop into a loop
perm := ();
if (not IsLoop( Q )) and (not MultiplicativeNeutralElement( Q )=fail) then
p := Position( Elements( Q ), MultiplicativeNeutralElement( Q ) );
if p<>1 then
perm := (1,p);
fi;
Q := IntoLoop( Q );
fi;
T := CayleyTable( Q );
# Calculating 9 invariants for three cases: quasigroup, loop, power associative loop
# I[i] will contain the invariant vector for ith element of Q
I := List( [1..n], i -> 0*[1..9] );
# invariant 1
# to distinguish the 3 cases
if (not IsLoop( Q ) )
then case := 1;
elif (not IsPowerAssociative( Q ))
then case := 2;
else
case := 3;
fi;
for i in [1..n] do I[i][1] := case; od;
# invariant 2
# for given x, cycle structure of L_x, R_x
for i in [1..n] do
I[i][2] := [
CycleStructurePerm( PermList( List( [1..n], j -> T[i][j]) ) ),
CycleStructurePerm( PermList( List( [1..n], j -> T[j][i]) ) )
];
od;
# invariant 3
if case = 1 then # am I an idempotent?
for i in [1..n] do I[i][3] := T[i][i]=i; od;
elif case = 2 then # am I an involution?
for i in [1..n] do I[i][3] := T[i][i]=1; od;
else # what's my order?
for i in [1..n] do I[i][3] := Order( Elements( Q )[i] ); od;
fi;
# invariant 4
if case <> 3 then # how many times am I a square ?
for i in [1..n] do j := T[i][i]; I[j][4] := I[j][4] + 1; od;
else # how many times am I a square, third power, fourth power?
for i in [1..n] do I[i][4] := [0,0,0]; od;
for i in [1..n] do
j := T[i][i]; I[j][4][1] := I[j][4][1] + 1;
j := T[i][j]; I[j][4][2] := I[j][4][2] + 1;
j := T[i][j]; I[j][4][3] := I[j][4][3] + 1;
od;
fi;
# invariant 5
if case <> 3 then # with how many elements do I commute?
for i in [1..n] do
I[i][5] := Length( Filtered( [1..n], j -> T[i][j] = T[j][i] ) );
od;
else # with how many elements of given order do I commute?
ebo := List( [1..n], i -> Filtered( [1..n], j -> I[j][3]=i ) ); # elements by order. PROG: must point to order invariant
ebo := Filtered( ebo, x -> not IsEmpty( x ) );
for i in [1..n] do
I[i][5] := List( ebo, J -> Length( Filtered( J, j -> T[ i ][ j ] = T[ j ][ i ] ) ) );
od;
fi;
# invariant 6
# is it true that (x*x)*x = x*(x*x)?
for i in [1..n] do
I[i][6] := T[T[i][i]][i] = T[i][T[i][i]];
od;
# invariant 7
if case <> 3 then # for how many elements y is (x*x)*y = x*(x*y)?
for i in [1..n] do
I[i][7] := Length( Filtered( [1..n], j -> T[T[i][i]][j] = T[i][T[i][j]] ) );
od;
else # for how many elements y of given order is (x*x)*y=x*(x*y)
for i in [1..n] do
I[i][7] := List( ebo, J -> Length( Filtered( J, j -> T[T[i][i]][j] = T[i][T[j][i]] ) ) );
od;
fi;
# invariants 8 and 9 (these take longer)
if case <> 3 then # with how many pairs of elements do I associate in the first, second position?
for i in [1..n] do
for j in [1..n] do for k in [1..n] do
if T[i][T[j][k]] = T[T[i][j]][k] then I[i][8] := I[i][8] + 1; fi;
if T[j][T[i][k]] = T[T[j][i]][k] then I[i][9] := I[i][9] + 1; fi;
od; od;
od;
else # for how many pairs of elements of given orders do I associate in the first, second position?
for i in [1..n] do
I[i][8] := []; I[i][9] := [];
for js in ebo do for ks in ebo do
count1 := 0; count2 := 0;
for j in js do for k in ks do
if T[i][T[j][k]] = T[T[i][j]][k] then count1 := count1 + 1; fi;
if T[j][T[i][k]] = T[T[j][i]][k] then count2 := count2 + 1; fi;
od; od;
Add( I[i][8], count1 ); Add( I[i][9], count2 );
od; od;
od;
fi;
# all invariants have now been calculated
# note that it can be deduced from the invariants when an element is central, for instance
# setting up the first part of the discriminator (invariants with the number of occurence)
A := Collected( I );
P := Sortex( List( A, x -> x[2] ) ); # rare invariants will be listed first, but the set ordering of A is otherwise not disrupted
A := Permuted( A, P );
# setting up the second part of the discriminator (blocks of elements invariant under isomorphisms)
B := List( [1..Length(A)], j -> Filtered( [1..n], i -> I[i] = A[j][1] ) );
# if a non-declared loop was converted into a loop, correcting for this
if not perm = () then
B := List( B, x -> Set( x, i -> i^perm ) );
fi;
return [ A, B ];
end);
#############################################################################
##
#F LOOPS_EfficientGenerators( Q, D )
##
## Auxiliary function.
## Given a quasigroup <Q> with discriminator <D>, it returns a list of
## indices of generators of <Q> deemed best for an isomorphism filter.
## It mimics the function SmallGeneratingSet, but it considers
## the elements in order determined by block size of the disciminator.
InstallGlobalFunction( LOOPS_EfficientGenerators,
function( Q, D )
local gens, sub, elements, candidates, max, S, best_gen, best_S;
gens := []; # generating set to be returned
sub := []; # substructure generated so far
elements := Concatenation( D[2] ); # all elements ordered by block size
candidates := ShallowCopy( elements ); # candidates for next generator
while sub <> Q do
# find an element not in sub that most enlarges sub
max := 0;
while not IsEmpty( candidates ) do
S := Subquasigroup( Q, Union( gens, [candidates[1]] ) );
if Size(S) > max then
max := Size( S );
best_gen := candidates[1];
best_S := S;
fi;
# discard elements of S since they cannot do better
candidates := Filtered( candidates, x -> not Elements(Q)[x] in S );
od;
Add( gens, best_gen );
sub := best_S;
# reset candidates for next round
candidates := Filtered( elements, x -> not Elements(Q)[x] in sub );
od;
return gens;
end);
#############################################################################
##
#O AreEqualDicriminators( D, E )
##
## Returns true if the invarinats of the two discriminators are the same,
## including number of occurrences of each invariant.
InstallMethod( AreEqualDiscriminators, "for two lists (discrimninators)",
[ IsList, IsList ],
function( D, E )
return D[ 1 ] = E[ 1 ];
end);
#############################################################################
## EXTENDING MAPPINS (AUXILIARY)
## -------------------------------------------------------------------------
# Here, we identity the map f: A --> B with the triple [ m, a, b ],
# where a is a subset of A, b[ i ] is the image of a[ i ], and m[ i ] > 0
# if and only if i is in a.
#############################################################################
##
#F LOOPS_ExtendHomomorphismByClosingSource( f, L, M )
##
## Auxiliary.
## <L>, <M> are multiplication tables of quasigroups, <f> is a partial map
## from a subset of elements of <L> to a subset of elements of <M>.
## This function attempts to extend <f> into a homomorphism of quasigroups by
## extending the source of <f> into (the smallest possible) subqusigroup of <L>.
InstallGlobalFunction( LOOPS_ExtendHomomorphismByClosingSource,
function( f, L, M )
local oldS, newS, pairs, x, y, newNow, p, z, fz;
oldS := [ ];
newS := f[ 2 ];
repeat
pairs := [];
for x in oldS do for y in newS do
Add( pairs, [ x, y ] );
Add( pairs, [ y, x ] );
od; od;
for x in newS do for y in newS do
Add( pairs, [ x, y ] );
od; od;
newNow := [];
for p in pairs do
x := p[ 1 ];
y := p[ 2 ];
z := L[ x ][ y ];
fz := M[ f[ 1 ][ x ] ][ f[ 1 ][ y ] ];
if f[ 1 ][ z ] = 0 then
f[ 1 ][ z ] := fz; AddSet( f[ 2 ], z ); AddSet( f[ 3 ], fz );
Add( newNow, z );
else
if not f[ 1 ][ z ] = fz then return fail; fi;
fi;
od;
oldS := Union( oldS, newS );
newS := ShallowCopy( newNow );
until IsEmpty( newS );
return f;
end);
#############################################################################
##
#F LOOPS_SublistPosition( S, x )
##
## auxiliary function
## input: list of lists <S>, element <x>
## returns: smallest i such that x in S[i]; or fail.
InstallGlobalFunction( LOOPS_SublistPosition,
function( S, x )
local i;
for i in [ 1..Length( S ) ] do if x in S[ i ] then return i; fi; od;
return fail;
end);
#############################################################################
##
#F LOOPS_ExtendIsomorphism( f, L, GenL, DisL, M, DisM )
##
## Auxiliary.
## Given a partial map <f> from a quasigroup <L> to a quasigroup <M>,
## it attempts to extend <f> into an isomorphism betweem <L> and <M>.
## <GenL>, <DisL> and <DisM> are precalculated and stand for:
## efficient generators of <L>, invariant subsets of <L>, efficient generators
## of <M>, respectively.
InstallGlobalFunction( LOOPS_ExtendIsomorphism,
function( f, L, GenL, DisL, M, DisM )
local x, possible_images, y, g;
f := LOOPS_ExtendHomomorphismByClosingSource( f, L, M );
if f = fail or Length( f[ 2 ] ) > Length( f[ 3 ] ) then return fail; fi;
if Length( f[ 2 ] ) = Length( L ) then return f; fi; #isomorphism found
x := GenL[ 1 ];
GenL := GenL{[2..Length(GenL)]};
possible_images := Filtered( DisM[ LOOPS_SublistPosition( DisL, x ) ], y -> not y in f[ 3 ] );
for y in possible_images do
g := StructuralCopy( f );
g[ 1 ][ x ] := y; AddSet( g[ 2 ], x ); AddSet( g[ 3 ], y );
g := LOOPS_ExtendIsomorphism( g, L, GenL, DisL, M, DisM );
if not g = fail then return g; fi; #isomorphism found
od;
return fail;
end);
#############################################################################
## ISOMORPHISMS OF LOOPS
## -------------------------------------------------------------------------
#############################################################################
##
#O IsomorphismQuasigroupsNC( L, GenL, DisL, M, DisM )
##
## Auxiliary. Given a quasigroup <L>, its efficient generators <GenL>, the
## disciminator <DisL> of <L>, and another loop <M> with discriminator
## <DisM>, it returns an isomorophism from <L> onto <M>, or it fails.
InstallGlobalFunction( IsomorphismQuasigroupsNC,
function( L, GenL, DisL, M, DisM )
local map, iso;
if not AreEqualDiscriminators( DisL, DisM ) then return fail; fi;
#mapping
map := 0 * [ 1.. Size( L ) ];
if IsLoop( L ) and IsLoop( M ) then
map[ 1 ] := 1; # identity element is certainly preserved
iso := LOOPS_ExtendIsomorphism( [ map, [ 1 ], [ 1 ] ], CayleyTable( L ), GenL, DisL[2], CayleyTable( M ), DisM[2] );
else
iso := LOOPS_ExtendIsomorphism( [ map, [ ], [ ] ], CayleyTable( L ), GenL, DisL[2], CayleyTable( M ), DisM[2] );
fi;
if not iso = fail then return SortingPerm( iso[ 1 ] ); fi;
return fail;
end);
#############################################################################
##
#O IsomorphismQuasigroups( L, M )
##
## If the quasigroups <L>, <M> are isomorophic, it returns an isomorphism
## from <L> onto <M>. Fails otherwise.
InstallMethod( IsomorphismQuasigroups, "for two quasigroups",
[ IsQuasigroup, IsQuasigroup ],
function( L, M )
local GenL1, GenL2, GenL, DisL, DisM, permL, permM, p, iso;
# making sure the quasigroups have canonical Cayley tables
if not L = Parent( L ) then L := CanonicalCopy( L ); fi;
if not M = Parent( M ) then M := CanonicalCopy( M ); fi;
# turning non-declared loops into loops
permL := ();
if (not IsLoop( L )) and (not MultiplicativeNeutralElement( L )=fail) then
p := Position( Elements( L ), MultiplicativeNeutralElement( L ) );
if p<>1 then
permL := (1,p);
fi;
L := IntoLoop( L );
fi;
permM := ();
if (not IsLoop( M )) and (not MultiplicativeNeutralElement( M )=fail) then
p := Position( Elements( M ), MultiplicativeNeutralElement( M ) );
if p<>1 then
permM := (1,p);
fi;
M := IntoLoop( M );
fi;
DisL := Discriminator( L );
GenL := LOOPS_EfficientGenerators( L, DisL );
DisM := Discriminator( M );
iso := IsomorphismQuasigroupsNC( L, GenL, DisL, M, DisM );
if not iso = fail then
iso := permL*iso*permM; # accounting for possible internal conversions to loops
fi;
return iso;
end);
#############################################################################
##
#O IsomorphismLoops( L, M )
##
## If the loops <L>, <M> are isomorophic, it returns an isomorphism
## from <L> onto <M>. Fails otherwise.
InstallMethod( IsomorphismLoops, "for loops",
[ IsLoop, IsLoop ],
function( L, M )
return IsomorphismQuasigroups( L, M );
end);
#############################################################################
##
#O QuasigroupsUpToIsomorphism( ls )
##
## Given a list <ls> of quasigroups, returns a sublist of <ls> consisting
## of represenatives of isomorphism classes of <ls>.
InstallMethod( QuasigroupsUpToIsomorphism, "for a list of quasigroups",
[ IsList ],
function( ls )
local i, quasigroups, L, D, G, with_same_D, is_new_quasigroup, K;
# making sure only quasigroups are on the list
if not IsEmpty( Filtered( ls, x -> not IsQuasigroup( x ) ) ) then
Error("LOOPS: <1> must be a list of quasigroups");
fi;
# special case: one quasigroup
if Length( ls ) = 1 then
return ls;
fi;
# making everything canonical
ls := ShallowCopy( ls ); # otherwise a side efect occurs in ls
for i in [1..Length(ls)] do
if not Parent(ls[i]) = ls[i] then
ls[i] := CanonicalCopy( ls[i] );
fi;
od;
quasigroups := [];
for L in ls do
D := Discriminator( L );
G := LOOPS_EfficientGenerators( L, D );
# will be testing only quasigroups with the same discriminator
with_same_D := Filtered( quasigroups, K -> AreEqualDiscriminators( K[2], D ) );
is_new_quasigroup := true;
for K in with_same_D do
if not IsomorphismQuasigroupsNC( L, G, D, K[1], K[2] ) = fail then
is_new_quasigroup := false;
break;
fi;
od;
if is_new_quasigroup then Add( quasigroups, [ L, D ] ); fi;
od;
# returning only quasigroups, not their discriminators
return List( quasigroups, L -> L[1] );
end);
#############################################################################
##
#O LoopsUpToIsomorphism( ls )
##
## Given a list <ls> of loops, returns a sublist of <ls> consisting
## of represenatives of isomorphism classes of <ls>.
InstallMethod( LoopsUpToIsomorphism, "for a list of loops",
[ IsList ],
function( ls )
# making sure only quasigroups are on the list
if not IsEmpty( Filtered( ls, x -> not IsQuasigroup( x ) ) ) then
Error("LOOPS: <1> must be a list of quasigroups");
fi;
return QuasigroupsUpToIsomorphism( ls );
end);
#############################################################################
##
#O IsomorphicCopyByPerm( Q, p )
##
## If <Q> is a quasigroup of order n and <p> a permutation of [1..n], returns
## the quasigroup (Q,*) such that p(xy) = p(x)*p(y).
## If <Q> is a loop, p is first composed with (1,1^p) to make sure
## that the neutral element of (Q,*) remains 1.
InstallMethod( IsomorphicCopyByPerm, "for a quasigroup and permutation",
[ IsQuasigroup, IsPerm ],
function( Q, p )
local ctQ, ct, inv_p;
ctQ := CanonicalCayleyTable( CayleyTable( Q ) );
# if Q is a loop and 1^p > 1, must normalize
if (IsLoop( Q ) and (not 1^p = 1)) then
p := p * (1, 1^p );
fi;
inv_p := Inverse( p );
ct := List([1..Size(Q)], i-> List([1..Size(Q)], j ->
( ctQ[ i^inv_p ][ j^inv_p ] )^p
) );
if IsLoop( Q ) then return LoopByCayleyTable( ct ); fi;
return QuasigroupByCayleyTable( ct );
end);
#############################################################################
##
#O IsomorphicCopyByNormalSubloop( L, S )
##
## Given a loop <L> and its normal subloop <S>, it returns an isomorphic
## copy of <L> with elements reordered according to right cosests of S
InstallMethod( IsomorphicCopyByNormalSubloop, "for two loops",
[ IsLoop, IsLoop ],
function( L, S )
local p;
if not IsNormal( L, S ) then
Error( "LOOPS: <2> must be a normal subloop of <1>");
fi;
# (PROG) SortingPerm is used rather than PermList since L is not necessarily canonical
p := Inverse( SortingPerm( PosInParent( Concatenation( RightCosets( L, S ) ) ) ) );
return IsomorphicCopyByPerm( L, p );
end);
#############################################################################
## AUTOMORPHISMS AND AUTOMORPHISM GROUPS
## -------------------------------------------------------------------------
#############################################################################
##
#F LOOPS_AutomorphismsFixingSet( S, Q, GenQ, DisQ )
##
## Auxiliary function.
## Given a quasigroup <Q>, its subset <S>, the efficient generators
## <GenQ> of <Q> and and invariant subsets <DisQ> of <Q>, it returns all
## automorphisms of <Q> fixing the set <S> pointwise.
InstallGlobalFunction( LOOPS_AutomorphismsFixingSet,
function( S, Q, GenQ, DisQ )
local n, x, A, possible_images, y, i, map, g;
# this is faster than extending a map
n := Size( Q );
S := Subquasigroup( Q, S ); # can be empty if Q is not a loop
if Size( S ) = n then return []; fi; # identity, no need to return
S := List( S, x -> Position( Elements( Q ), x ) );
#pruning blocks
DisQ := List( DisQ, B -> Filtered( B, x -> not x in S ) );
# first unmapped generator
x := GenQ[ 1 ];
GenQ := GenQ{[2..Length(GenQ)]};
A := [];
possible_images := Filtered( DisQ[ LOOPS_SublistPosition( DisQ, x ) ], y -> y <> x );
for y in possible_images do
# constructing map
map := 0*[1..n];
for i in [1..n] do if i in S then map[ i ] := i; fi; od;
map[ x ] := y;
g := [ map, Union( S, [ x ] ), Union( S, [ y ] ) ];
# extending map
g := LOOPS_ExtendIsomorphism( g, CayleyTable( Q ), GenQ, DisQ, CayleyTable( Q ), DisQ );
if not g = fail then AddSet( A, g[ 1 ] ); fi;
od;
S := Union( S, [ x ] );
return Union( A, LOOPS_AutomorphismsFixingSet( S, Q, GenQ, DisQ ) );
end);
#############################################################################
##
#F AutomorphismGroup( Q )
##
## Returns the automorphism group of a quasigroup <Q>,
## as a permutation group on [1..Size(Q)].
InstallOtherMethod( AutomorphismGroup, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local DisQ, GenQ, A;
# making sure Q has canonical Cayley table
if not Q = Parent( Q ) then Q := CanonicalCopy( Q ); fi;
DisQ := Discriminator( Q );
GenQ := LOOPS_EfficientGenerators( Q, DisQ );
if IsLoop( Q ) then
A := LOOPS_AutomorphismsFixingSet( [ 1 ], Q, GenQ, DisQ[2] );
else
A := LOOPS_AutomorphismsFixingSet( [ ], Q, GenQ, DisQ[2] );
fi;
if IsEmpty( A ) then return Group( () ); fi; # no notrivial automorphism
return Group( List( A, p -> SortingPerm( p ) ) );
end);
#############################################################################
## ISOTOPISM OF LOOPS
## ------------------------------------------------------------------------
#############################################################################
##
#O IsotopismLoops( L1, L2 )
##
## If L1, L2 are isotopic loops, returns true, else fail.
# (MATH) First we calculate all principal loop isotopes of L1 of the form
# PrincipalLoopIsotope(L1, f, g), where f, g, are elements of L1.
# Then we filter these up to isomorphism. If L2 is isotopic to L1, then
# L2 is isomorphic to one of these principal isotopes.
InstallMethod( IsotopismLoops, "for two loops",
[ IsLoop, IsLoop ],
function( L1, L2 )
local istps, fg, f, g, L, phi, pos, alpha, beta, gamma, p;
# make all loops canonical to be able to calculate isotopisms
if not L1 = Parent( L1 ) then L1 := LoopByCayleyTable( CayleyTable( L1 ) ); fi;
if not L2 = Parent( L2 ) then L2 := LoopByCayleyTable( CayleyTable( L2 ) ); fi;
# first testing for isotopic invariants
if not Size(L1)=Size(L2) then return fail; fi;
if IsomorphismLoops( Center(L1), Center(L2) ) = fail then return fail; fi;
if IsomorphismLoops( LeftNucleus(L1), LeftNucleus(L2) ) = fail then return fail; fi;
if IsomorphismLoops( RightNucleus(L1), RightNucleus(L2) ) = fail then return fail; fi;
if IsomorphismLoops( MiddleNucleus(L1), MiddleNucleus(L2) ) = fail then return fail; fi;
# we could test for isomorphism among multiplication groups and inner mapping group, too
if not Size(MultiplicationGroup(L1)) = Size(MultiplicationGroup(L2)) then return fail; fi;
if not Size(InnerMappingGroup(L1)) = Size(InnerMappingGroup(L2)) then return fail; fi;
# now trying to construct an isotopism
istps := [];
fg := [];
for f in L1 do for g in L1 do
Add(istps, PrincipalLoopIsotope( L1, f, g ));
Add(fg, [ f, g ] );
od; od;
for L in LoopsUpToIsomorphism( istps ) do
phi := IsomorphismLoops( L, L2 );
if not phi = fail then
# must reconstruct the isotopism (alpha, beta, gamma)
# first figure out what f and g were
pos := Position( istps, L );
f := fg[ pos ][ 1 ];
g := fg[ pos ][ 2 ];
alpha := RightTranslation( L1, g );
beta := LeftTranslation( L1, f );
# we also applied an isomorphism (1,f*g) inside PrincipalLoopIsotope
p := Position( L1, f*g );
gamma := ();
if p > 1 then
alpha := alpha * (1,p);
beta := beta * (1,p);
gamma := gamma * (1,p);
fi;
# finally, we apply the isomorphism phi
alpha := alpha * phi;
beta := beta * phi;
gamma := gamma * phi;
return [ alpha, beta, gamma ];
fi;
od;
return fail;
end);
#############################################################################
##
#O LoopsUpToIsotopism( ls )
##
## Given a list <ls> of loops, returns a sublist of <ls> consisting
## of represenatives of isotopism classes of <ls>. VERY SLOW!
InstallMethod( LoopsUpToIsotopism, "for a list of loops",
[ IsList ],
function( ls )
local loops, L, is_new_loop, K, M, istps, f, g;
# making sure only loops are on the list
if not IsEmpty( Filtered( ls, x -> not IsLoop( x ) ) ) then
Error("LOOPS: <1> must be a list of loops");
fi;
loops := [];
for L in ls do
is_new_loop := true;
# find all principal isotopes of L up to isomorphism
istps := [];
for f in L do for g in L do
Add(istps, PrincipalLoopIsotope( L, f, g ) );
od; od;
istps := LoopsUpToIsomorphism(istps);
# check if any is isomorphic to a found loop
for K in loops do for M in istps do
if not IsomorphismLoops( K, M ) = fail then
is_new_loop := false; break;
fi;
od; od;
if is_new_loop then Add( loops, L ); fi;
od;
return loops;
end);

11
gap/memory.gd Normal file
View file

@ -0,0 +1,11 @@
############################################################################
##
#W memory.gd Memory management [loops]
##
#H @(#)$Id: memory.gd, v 3.0.0 2015/06/18 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareGlobalFunction( "LOOPS_FreeMemory" );

31
gap/memory.gi Normal file
View file

@ -0,0 +1,31 @@
#############################################################################
##
#W memory.gi Memory management [loops]
##
#H @(#)$Id: memory.gi, v 3.3.0 2016/10/20 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#F LOOPS_FreeMemory( )
##
## Frees memory by unbinding some global variables, mostly those
## declared during activation of libraries.
## Returns the amount of freed memory in kbytes.
InstallGlobalFunction( LOOPS_FreeMemory, function( )
# RCC loops
LOOPS_rcc_transitive_groups := [];
LOOPS_rcc_sections := List( [1..Length(LOOPS_rcc_data[1])], i-> [] );
LOOPS_rcc_conjugacy_classes := [ [], [] ];
# automorphic loops
LOOPS_automorphic_cocycles := [];
LOOPS_automorphic_coordinates := [];
GASMAN("collect");
return GasmanStatistics().full.deadkb;
end);

17
gap/mlt_search.gd Normal file
View file

@ -0,0 +1,17 @@
#############################################################################
##
#W mlt_search.gd Realizing groups as multiplication groups of loops [loops]
##
#H @(#)$Id: mlt_search.gd, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareOperation( "AllLoopTablesInGroup", [IsGroup] );
DeclareOperation( "AllProperLoopTablesInGroup", [IsGroup] );
DeclareOperation( "OneLoopTableInGroup", [IsGroup] );
DeclareOperation( "OneProperLoopTableInGroup", [IsGroup] );
DeclareOperation( "AllLoopsWithMltGroup", [IsGroup] );
DeclareOperation( "OneLoopWithMltGroup", [IsGroup] );

607
gap/mlt_search.gi Normal file
View file

@ -0,0 +1,607 @@
#############################################################################
##
#W mlt_search.gi Realizing groups as multiplication groups of loops [loops]
##
#H @(#)$Id: mlt_search.gi, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
##########################################################################
#
# The following commands look for loops such that the
# multiplication group is contained in a given group <G>.
#
# The resulting loops are given by their right sections.
#
# One can speed up the search by setting the argument <depth>
# higher; the price is much higher memory consumption.
#
# <depth> is optimally choosen if in the permutation group <G>
# there are "not many" permutations fixing <depth> elements.
#
# You can omit this argument, or set depth=2 with no harm.
#
# The parameter <infolevel> determines the amount of information
# you get during the search.
#
# With infolevel=1, you get the information on timing and hits.
# With infolevel=2, the results are printed, as well.
#
##########################################################################
#
# AllLoopTablesInGroup(G[, depth[, infolevel]] )
# returns all loop tables <T> such that Mlt(T)<=G
#
# AllProperLoopTablesInGroup(G[, depth[, infolevel]] )
# returns all non-associative loop tables <T> such that Mlt(T)<=G
#
# OneLoopTableInGroup(G[, depth[, infolevel]] )
# returns one loop table <T> such that Mlt(T)<=G
#
# OneProperLoopTableInGroup(G[, depth[, infolevel]] )
# returns one non-associative loop table <T> such that Mlt(T)<=G
#
# AllLoopsWithMltGroup(G[, depth[, infolevel]] )
# returns all loop tables <T> such that Mlt(T)=G
#
# OneLoopWithMltGroup(G[, depth[, infolevel]] )
# returns one loop table <T> such that Mlt(T)=G
#
##########################################################################
# Debugging
LOOPS_SearchRuntime:=function(tstart)
return Concatenation("[ ", StringTime(Runtime()-tstart)," ]");
end;
LOOPS_SearchInfo:=NewInfoClass("InfoCayleySearch");
SetInfoLevel(LOOPS_SearchInfo,1);
# LOOPS_TableSearchNC(G, depth, infolevel, task)
# restiction in ["", "all", "all proper", "one", "one proper", "all exact", "one exact"]
# if task="" then task:="all";
# auxiliary function
LOOPS_TableSearchNC:=function(g, ldepth, infolevel, task)
local
# task variables
only_one, only_proper, only_exact, takeit,
# local external variables
results, V, hash, row, pi, col_chunks, degree, level, depth,
# local scanner functions
move_right, next_node, possible_next_rows,
# main search loop variables
fpf_classes, reps, x0, x1, k, i, j, ct, ls, rs,
# debugging
old_infolevel,time_start;
# setting the local external variables
results:=[];
V:=[];
hash:=[];
row:=[];
pi:=[];
col_chunks:=[];
degree:=0;
level:=0;
depth:=0;
# functions
move_right:=function()
local i;
if level=0 then
return false;
fi;
if pi[level]<>[] then
row[level]:=Remove(pi[level],1);
for i in [1..degree] do
col_chunks[i][level]:=i^V[row[level]];
od;
return true;
else
Unbind(row[level]);
Unbind(pi[level]);
for i in [1..degree] do
Unbind(col_chunks[i][level]);
od;
level:=level-1;
return move_right();
fi;
end;
# next_node()
# we call it when
# a) row[level+1] and pi[level+1] are not defined ---> MOVE RIGHT
# b) row[level+1] is not defined and pi[level+1] is defined
# and non empty ---> MOVE DOWN
next_node:=function()
local i;
if IsBound(pi[level+1])
and (not IsBound(row[level+1]))
and (pi[level+1]<>[])
then
level:=level+1;
row[level]:=Remove(pi[level],1);
for i in [1..degree] do
col_chunks[i][level]:=i^V[row[level]];
od;
return true;
elif (not IsBound(pi[level+1])) and (not IsBound(row[level+1])) then
return move_right();
else
return fail;
fi;
end;
possible_next_rows:=function()
local np,vp,dp;
dp:=Minimum(level, depth);
np:=List([2..degree],i->Sum([1..dp],j->(col_chunks[i][j]-1)*degree^(dp-j))+1);
if ForAny(np,i->not(IsBound(hash[dp][i]))) then
pi[level+1]:=[];
return false;
fi;
np:=List(np,i->[hash[dp][i][1]..hash[dp][i][1]+hash[dp][i][2]]);
np:=List(np,x->Set(V{x},y->(level+1)^y));
vp:=List(Cartesian(np{[1..depth-1]}),x->Concatenation([level+1],x));
vp:=List(vp,x->Sum([1..depth],j->(x[j]-1)*degree^(depth-j))+1);
vp:=Filtered(vp,i->IsBound(hash[depth][i]));
vp:=List(vp,i->[hash[depth][i][1]..hash[depth][i][1]+hash[depth][i][2]]);
vp:=Filtered(Concatenation(vp),x->
ForAll([2..degree],i->(i^V[x] in np[i-1]) and not(i^V[x] in col_chunks[i]))
);
pi[level+1]:=vp;
return vp<>[];
end;
# main search part
# printing the task ["", "all", "all proper", "one", "one proper", "all exact", "one exact"]
old_infolevel:=InfoLevel(LOOPS_SearchInfo);
SetInfoLevel(LOOPS_SearchInfo,infolevel);
if task="" then task:="all"; fi;
if task="all" then
only_one:=false;
only_proper:=false;
only_exact:=false;
Info(LOOPS_SearchInfo, 1, "### Search for all loops in the given group ###");
elif task="all proper" then
only_one:=false;
only_proper:=true;
only_exact:=false;
Info(LOOPS_SearchInfo, 1, "### Search for all nonassociative loops in the given group ###");
elif task="one" then
only_one:=true;
only_proper:=false;
only_exact:=false;
Info(LOOPS_SearchInfo, 1, "### Search for one loop in the given group ###");
elif task="one proper" then
only_one:=true;
only_proper:=true;
only_exact:=false;
Info(LOOPS_SearchInfo, 1, "### Search for one nonassociative loops loops in the given group ###");
elif task="all exact" then
only_one:=false;
only_proper:=false;
only_exact:=true;
Info(LOOPS_SearchInfo, 1, "### Search for all loops with given multiplication group ###");
elif task="one exact" then
only_one:=true;
only_proper:=false;
only_exact:=true;
Info(LOOPS_SearchInfo, 1, "### Search for one loop with given multiplication group ###");
else
SetInfoLevel(LOOPS_SearchInfo,old_infolevel);
return fail;
fi;
time_start:=Runtime();
degree:=NrMovedPoints(g);
depth:=ldepth;
Info(LOOPS_SearchInfo, 1, "# Size of the input group: ", Size(g));
Info(LOOPS_SearchInfo, 1, "# Degree of the permutation group: ", degree);
# analysis of the fixed point free elements
fpf_classes:=Filtered(ConjugacyClasses(g),x->NrMovedPoints(Representative(x))=degree);
if Length(fpf_classes)=1 then
V:=Elements(fpf_classes[1]);
else
V:=Union(fpf_classes);;
fi;
MakeImmutable(V);
reps:=List(fpf_classes,x->Minimum(Elements(x)));
reps:=Set(reps,x->Position(V,x));
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Search started.");
Info(LOOPS_SearchInfo, 1, "# Collected the fixed point free elements." );
Info(LOOPS_SearchInfo, 1, "# Number of conjugugacy classes = ", Size(reps), "." );
Info(LOOPS_SearchInfo, 1, "# Number of fixed point free elements = ", Size(V), "." );
# hash tables
hash:=List([1..depth],i->[]);
x0:=0*[1..depth];
k:=0*[1..depth];
for j in [1..Length(V)] do
x1:=List([1..depth],i->i^V[j]);
for i in [1..depth] do
if x0{[1..i]}<>x1{[1..i]} then
k[i]:=Sum([1..i],t->(x1[t]-1)*degree^(i-t))+1;
hash[i][k[i]]:=[j,0];
else
hash[i][k[i]][2]:=hash[i][k[i]][2]+1;
fi;
od;
x0:=ShallowCopy(x1);
od;
MakeImmutable(hash);
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Hash table of depth ", depth, " created." );
# initialization
row:=['*'];
pi:=[[],ShallowCopy(reps)];
col_chunks:=List([1..degree],i->[i]);
level:=1;
results:=[];
while next_node() do
if level=2 then
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start),
" We have ", Length(pi[2])+1, " more step(s)." );
fi;
if level=degree then
ct:=List(V{row{[2..degree]}},ListPerm);
ct:=Concatenation([[1..degree]],ct);
rs:=Immutable(List(ct,PermList));
ls:=Immutable(List(TransposedMat(ct),PermList));
if only_exact then
takeit:=(Group(Union(ls,rs))=g);
elif only_proper then
takeit:=(ForAll(ls,y->y in g) and
not ForAll(rs,x->ForAll(x*rs,y->y in rs)));
else
takeit:=ForAll(ls,y->y in g);
fi;
if takeit then
Add(results, ShallowCopy(row));
Info(LOOPS_SearchInfo, 2, "##############################");
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start),
" Hit number ", Length(results));
Info(LOOPS_SearchInfo, 2, ct);
if only_one then break; fi;
fi;
else
possible_next_rows();
if pi[level+1]=[] then Unbind(pi[level+1]); fi;
fi;
od;
Info(LOOPS_SearchInfo, 1, "##############################");
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Finished. ", Length(results), " loops found." );
SetInfoLevel(LOOPS_SearchInfo,old_infolevel);
return List(results,x->Concatenation([()],V{x{[2..degree]}}));
end;
# auxiliary function
LOOPS_SearchInputCheck:=function( G, depth, infolevel, task )
local degree;
if not IsPermGroup(G) then
Info( InfoWarning, 1, "<G> must be a permutation group" );
return false;
fi;
degree:=NrMovedPoints(G);
if MovedPoints(G)<>[1..degree] then
Info( InfoWarning, 1, "<G> must act transitively on [1..degree]" );
return false;
fi;
if depth=fail then depth:=LogInt(Size(G),degree)-1; fi;
if not IsInt(depth) or depth<=0 then
Info( InfoWarning, 1, "<depth> must be a positive integer" );
return false;
fi;
if infolevel=fail then infolevel:=1; fi;
if not IsInt(infolevel) or infolevel<0 then
Info( InfoWarning, 1, "<infolevel> must be a positive integer" );
return false;
fi;
if not task in ["", "all", "all proper", "one", "one proper", "all exact", "one exact"] then
Info( InfoWarning, 1, "<task> must be one of the following:");
Info( InfoWarning, 1, "\t\"\", \"all\", \"all proper\", \"one\", ",
"\"one proper\", \"all exact\", \"one exact\"." );
return false;
fi;
return [G,depth,infolevel,task];
end;
#############################################################################
##
#O AllLoopTablesInGroup(G[, depth[, infolevel]] )
##
## returns all loop tables <T> such that Mlt(T)<=G.
InstallMethod( AllLoopTablesInGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="all";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllLoopTablesInGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="all";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllLoopTablesInGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="all";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
#############################################################################
##
#O AllProperLoopTablesInGroup(G[, depth[, infolevel]] )
##
## returns all non-associative loop tables <T> such that Mlt(T)<=G.
InstallMethod( AllProperLoopTablesInGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="all proper";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllProperLoopTablesInGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="all proper";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllProperLoopTablesInGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="all proper";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
#############################################################################
##
#O OneLoopTableInGroup(G[, depth[, infolevel]] )
##
## returns one loop table <T> such that Mlt(T)<=G.
InstallMethod( OneLoopTableInGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="one";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneLoopTableInGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="one";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneLoopTableInGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="one";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
#############################################################################
##
#O OneProperLoopTableInGroup(G[, depth[, infolevel]] )
##
## returns one non-associative loop table <T> such that Mlt(T)<=G.
InstallMethod( OneProperLoopTableInGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="one proper";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneProperLoopTableInGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="one proper";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneProperLoopTableInGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="one proper";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
#############################################################################
##
#O AllLoopsWithMltGroup(G[, depth[, infolevel]] )
##
## returns all loop tables <T> such that Mlt(T)=G.
InstallMethod( AllLoopsWithMltGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="all exact";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllLoopsWithMltGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="all exact";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( AllLoopsWithMltGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="all exact";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
#############################################################################
##
#O OneLoopWithMltGroup(G[, depth[, infolevel]] )
##
## returns one loop table <T> such that Mlt(T)=G.
InstallMethod( OneLoopWithMltGroup, "for a group",
[ IsGroup ],
function( G )
local depth, infolevel, task, a;
task:="one exact";
depth:=fail;
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneLoopWithMltGroup, "for a group and integer",
[ IsGroup, IsInt ],
function( G, depth )
local infolevel, task, a;
task:="one exact";
infolevel:=fail;
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);
InstallOtherMethod( OneLoopWithMltGroup, "for a group and two integers",
[ IsGroup, IsInt, IsInt ],
function( G, depth, infolevel )
local task, a;
task:="one exact";
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
if a<>false then
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
else
return fail;
fi;
end);

View file

@ -0,0 +1,31 @@
############################################################################
##
#W moufang_modifications.gd Moufang modifications [loops]
##
#H @(#)$Id: moufang_modifications.gd, v 2.0.0 2008/01/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## LOOPS M(G,2)
## -------------------------------------------------------------------------
DeclareGlobalFunction( "LoopMG2", IsGroup );
#############################################################################
## CYCLIC MODIFICATION
## -------------------------------------------------------------------------
DeclareGlobalFunction( "LoopByCyclicModification" );
#############################################################################
## DIHEDRAL MODIFICATION
## -------------------------------------------------------------------------
DeclareGlobalFunction( "LoopByDihedralModification" );
#############################################################################
## AUXILIARY
## -------------------------------------------------------------------------
DeclareGlobalFunction( "LOOPS_PositionList" );
DeclareGlobalFunction( "LOOPS_Modular" );
DeclareGlobalFunction( "LOOPS_DVSigma" );

View file

@ -0,0 +1,218 @@
#############################################################################
##
#W moufang_modifications.gi Moufang modifications [loops]
##
#H @(#)$Id: moufang_modifications.gi, v 3.0.0 2015/06/15 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## LOOPS M(G,2)
## -------------------------------------------------------------------------
#############################################################################
##
#F LoopMG2( G )
##
## Returns the Chein loop M(G,2) constructed from a group <G>.
## See documentation for details.
InstallGlobalFunction( LoopMG2, function( G )
local T, inv, n, L, i, j;
T := MultiplicationTable( Elements( G ) );
n := Size( G );
inv := List( [1..n], i->Position( T[i], 1 ) ); #inverses
L := List( [1..2*n], i->[]);
for i in [1..n] do for j in [1..n] do
L[ i ][ j ] := T[ i ][ j ]; # g*h = gh
L[ i ][ j+n ] := T[ j ][ i ] + n; # g*hu = (hg)u
L[ i+n ][ j ] := T[ i ][ inv[ j ] ] + n; # gu*h = (gh^{-1})u
L[ i+n ][ j+n ] := T[ inv[ j ] ][ i ]; # gu*hu = h^{-1}g
od; od;
return LoopByCayleyTable( L );
end);
#############################################################################
## AUXILIARY FUNCTIONS FOR MOUFANG MODIFICATIONS
## -------------------------------------------------------------------------
#############################################################################
##
#F LOOPS_PositionList( A, B )
##
## input: lists A, B
## returns: list P, where P[i] is the position of B[i] in A
InstallGlobalFunction( LOOPS_PositionList,
function( A, B )
local P, b;
P := [];
for b in B do Add( P, Position( A, b ) ); od;
return P;
end);
#############################################################################
##
#F LOOPS_Modular( i, m )
##
## returns i modulo the set [-m+1..m]
InstallGlobalFunction( LOOPS_Modular,
function(i, m)
while i>m do i:=i-2*m; od;
while i<1-m do i:=i+2*m; od;
return i;
end);
#############################################################################
##
#F LOOPS_DVSigma( i, m )
##
## Calculates overflow of i modulo [-m+1..m].
## See documentation for more details.
InstallGlobalFunction( LOOPS_DVSigma,
function( i, m )
if i > m then return 1; fi;
if i < 1 - m then return -1; fi;
return 0;
end);
#############################################################################
## CYCLIC MODIFICATION
## -------------------------------------------------------------------------
#############################################################################
##
#F LoopByCyclicModification( L, S, a, h)
##
## Returns the cyclic modification of Moufang loop <L> with parameters
## <S>, <a>, <h>, as described in the documentation.
# (MATH)
# L is a loop,
# S is a normal subloop of L, or a set generating a normal subloop of L,
# L/S is cyclic of order 2m, generated by aS,
# h is an element of Z(L) and S
# (PROG)
# NOTHING IS CHECKED!!
InstallGlobalFunction( LoopByCyclicModification, function( L, S, a, h )
local n, ih, m, aP, aa, i, iL, T, x, y, z, exponent;
# making sure that S is a subloop of L
if not IsLoop( S ) then S := Subloop( L, S ); fi;
# converting all into numbers for faster calculation
n := Size( L );
ih := Position( Elements( L ), h^(-1) ); #inverse of h
h := Position( Elements( L ), h );
a := Position( Elements( L ), a );
S := LOOPS_PositionList( Elements( L ), Elements( S ) );
L := CayleyTable( L );
# setting parameter m of the construction
m := n / ( 2 * Length( S ) );
# calculating all cosets a^i, for i in M
aP := []; aa := 1;
for i in [ 0..2*m-1 ] do
Add( aP, List( S, j -> L[ aa ][ j ] ) );
aa := L[ aa ][ a ];
od;
# into which cosets belong elements of L
iL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(aP, x ) - 1, m ) );
# setting up the multiplication table
T := List( [ 1..n ], i->[] );
for x in [ 1..n ] do for y in [ 1..n ] do
z := L[ x ][ y ];
exponent := LOOPS_DVSigma( iL[ x ] + iL[ y ], m );
if exponent = 1 then z := L[ z ][ h ]; fi;
if exponent = -1 then z := L[ z ][ ih ]; fi;
T[ x ][ y ] := z;
od; od;
return LoopByCayleyTable( T );
end);
#############################################################################
## DIHEDRAL MODIFICATION
## -------------------------------------------------------------------------
#############################################################################
##
#F LoopByDihedralModification( L, S, e, f, h)
##
## Returns the dihedral modification of Moufang loop <L> with parameters
## <S>, <e>, <f>, <h>, as described in the documentation.
# (MATH)
# L is a loop,
# S is a normal subloop of L, or a set generating a normal subloop of L,
# L/S is dihedral of order 4m,
# eS, fS are involutions of L/S such that eS*fS is of order 2m,
# let G0 be the union of cosets of L/S generated by eS*fS,
# h is an element of N(L), S and Z(G0).
# (PROG)
# NOTHING IS CHECKED!!
InstallGlobalFunction( LoopByDihedralModification, function( L, S, e, f, h )
local a, G0, n, m, ih, aP, aa, i, eP, fP, eL, fL, T, x, y, z, exp;
# making sure that S is a subloop of L
if not IsLoop( S ) then S := Subloop( L, S ); fi;
# obtaining a and G0
a := e * f;
G0:= Subloop( L, a * Elements( S ) );
# all in numbers
n := Size( L );
ih := Position( Elements( L ), h^(-1) ); #inverse of h
h := Position( Elements( L ), h );
a := Position( Elements( L ), a );
e := Position( Elements( L ), e );
f := Position( Elements( L ), f );
S := LOOPS_PositionList( Elements( L ), Elements( S ) );
G0 := LOOPS_PositionList( Elements( L ), Elements( G0 ) );
L := CayleyTable( L );
# setting parameter m
m := n / ( 4 * Length( S ) );
# powers of aS, eS and fS
aP := []; aa := 1;
for i in [ 0..2*m-1 ] do
Add( aP, List( S, i -> L[ aa ][ i ] ) );
aa := L[ aa ][ a ];
od;
eP := List( aP, x -> Union( List( x, y -> [ y, L[ e ][ y ] ] ) ) );
fP := List( aP, x -> Union( List( x, y -> [ y, L[ y ][ f ] ] ) ) );
# into which cosets belong elements of L
eL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(eP, x ) - 1, m ) );
fL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(fP, x ) - 1, m ) );
# setting up multiplication table
T := List( L, x->[] );
for x in [ 1..n ] do for y in [ 1..n ] do
if y in G0 then exp := LOOPS_DVSigma( eL[ x ] + fL[ y ], m );
else exp := (-1)*LOOPS_DVSigma( eL[ x ] + fL[ y ], m ); fi;
z := L[ x ][ y ];
if exp = 1 then z := L[ z ][ h ]; fi;
if exp = -1 then z := L[ z ][ ih ]; fi;
T[ x ][ y ] := z;
od; od;
return LoopByCayleyTable(T);
end);

12
gap/moufang_triality.gd Normal file
View file

@ -0,0 +1,12 @@
############################################################################
##
#W moufang_triality.gd Triality of Moufang loops [loops]
##
#H @(#)$Id: moufang_triality.gd, v 2.0.0 2008/01/21 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareGlobalFunction( "TrialityPermGroup" );
DeclareGlobalFunction( "TrialityPcGroup" );

98
gap/moufang_triality.gi Normal file
View file

@ -0,0 +1,98 @@
#############################################################################
##
#W moufang_triality.gi Triality of Moufang loops [loops]
##
#H @(#)$Id: moufang_triality.gi, v 3.0.0 2015/06/12 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#F TrialityPermGroup( L )
##
## Returns the triality group associated with Moufang loop <L>,
## as a permutation group.
InstallGlobalFunction( TrialityPermGroup, function( L )
local AA, BB, L_inv, i, trg_perm, trrho, trsigma;
if IsGroup( L ) then L:=IntoLoop( L ); fi;
if not( IsMoufangLoop( L ) ) then
Error( "LOOPS: <1> has to be a Moufang loop." );
fi;
trrho := Product( List( [1..Size(L)], k -> (k,k+Size(L),k+2*Size(L)) ) );
L_inv := PermList( List( [1..Size(L)], k -> 1^(LeftSection(L)[k]^-1) ) );
trsigma := L_inv^(trrho^2) *
Product( List( [1..Size(L)], k ->
(k, Size(L)+k^L_inv ) ) );
AA := [];
BB := [];
for i in PosInParent( GeneratorsSmallest( L ) ) do
Add( AA, (LeftSection(L)[i]*RightSection(L)[i])^-1
* LeftSection(L)[i]^trrho
* RightSection(L)[i]^(trrho^2) );
od;
BB := OnTuples( AA, trrho );
trg_perm := Group( Concatenation( AA, BB ) );
SetName(trg_perm, Concatenation(
"<Triality (dual collineation) group of order ",
StringPP( Order( trg_perm ) ),
">"
));
return rec( group := trg_perm, sigma := trsigma, rho := trrho );
end);
#############################################################################
##
#F TrialityPcGroup( L )
##
## Returns the triality group associated with Moufang loop <L>,
## as a pc group.
InstallGlobalFunction( TrialityPcGroup, function( L )
local trcoll, trcoll_pcgs, pc_gens, big_gr, big_pcgs,
trg_pc,sigma_pc,rho_pc;
if not( IsSolvable( MultiplicationGroup( L ) ) ) then
Error( "LOOPS: The triality group is not solvable." );
fi;
trcoll := TrialityPermGroup( L );
if IsNilpotent( L ) then
trcoll_pcgs := SpecialPcgs( trcoll.group );
else
trcoll_pcgs := Pcgs( trcoll.group );
fi;
pc_gens := PcgsByPcSequence(
FamilyObj( One( trcoll.group ) ),
Concatenation(
[ trcoll.sigma, trcoll.rho ],
trcoll_pcgs
)
);
big_gr := PcGroupWithPcgs( pc_gens );
big_pcgs := Pcgs( big_gr );
trg_pc := Group( big_pcgs{[3..Length(big_pcgs)]} );
sigma_pc := big_pcgs[1];
rho_pc := big_pcgs[2];
SetName(trg_pc, Concatenation(
"<Triality pc group of order ",
StringPP( Order( trg_pc ) ),
">"
));
return rec( group := trg_pc, sigma := sigma_pc, rho := rho_pc );
end);

104
gap/quasigroups.gd Normal file
View file

@ -0,0 +1,104 @@
#############################################################################
##
#W quasigroups.gd Representing, creating and displaying quasigroups [loops]
##
#H @(#)$Id: quasigroups.gd, v 3.2.0 2016/05/02 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## GAP CATEGORIES AND REPRESENTATIONS
## -------------------------------------------------------------------------
## element of a quasigroup
DeclareCategory( "IsQuasigroupElement", IsMultiplicativeElement );
DeclareRepresentation( "IsQuasigroupElmRep",
IsPositionalObjectRep and IsMultiplicativeElement, [1] );
## element of a loop
DeclareCategory( "IsLoopElement",
IsQuasigroupElement and IsMultiplicativeElementWithInverse );
DeclareRepresentation( "IsLoopElmRep",
IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] );
## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup)
DeclareCategory( "IsLatin", IsObject );
## quasigroup
DeclareCategory( "IsQuasigroup", IsMagma and IsLatin );
## loop
DeclareCategory( "IsLoop", IsQuasigroup and IsMultiplicativeElementWithInverseCollection);
#############################################################################
## TESTING MULTIPLICATION TABLES
## -------------------------------------------------------------------------
DeclareOperation( "IsQuasigroupTable", [ IsMatrix ] );
DeclareSynonym( "IsQuasigroupCayleyTable", IsQuasigroupTable );
DeclareOperation( "IsLoopTable", [ IsMatrix ] );
DeclareSynonym( "IsLoopCayleyTable", IsLoopTable );
DeclareOperation( "CanonicalCayleyTable", [ IsMatrix ] );
DeclareOperation( "NormalizedQuasigroupTable", [ IsMatrix ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS MANUALLY
## -------------------------------------------------------------------------
DeclareAttribute( "CayleyTable", IsQuasigroup );
DeclareOperation( "QuasigroupByCayleyTable", [ IsMatrix ] );
DeclareOperation( "LoopByCayleyTable", [ IsMatrix ] );
DeclareOperation( "SetQuasigroupElmName", [ IsQuasigroup, IsString ] );
DeclareSynonym( "SetLoopElmName", SetQuasigroupElmName );
DeclareOperation( "CanonicalCopy", [ IsQuasigroup ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS FROM A FILE
## -------------------------------------------------------------------------
DeclareOperation( "QuasigroupFromFile", [ IsString, IsString ] );
DeclareOperation( "LoopFromFile", [ IsString, IsString ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS
## -------------------------------------------------------------------------
DeclareOperation( "CayleyTableByPerms", [ IsPermCollection ] );
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
## --------------------------------------------------------------------------
#DirectProduct already declared for groups.
#############################################################################
## OPPOSITE QUASIGROUPS AND LOOPS
## --------------------------------------------------------------------------
DeclareOperation( "OppositeQuasigroup", [ IsQuasigroup ] );
DeclareOperation( "OppositeLoop", [ IsLoop ] );
DeclareAttribute( "Opposite", IsQuasigroup );
#############################################################################
## AUXILIARY
## --------------------------------------------------------------------------
DeclareGlobalFunction( "LOOPS_ReadCayleyTableFromFile" );
DeclareGlobalFunction( "LOOPS_CayleyTableByRightFolder" );

836
gap/quasigroups.gi Normal file
View file

@ -0,0 +1,836 @@
#############################################################################
##
#W quasigroups.gi Representing, creating and displaying quasigroups [loops]
##
#H @(#)$Id: creation.gi, v 3.2.0 2015/11/22 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
## TESTING MULTIPLICATION TABLES
## -------------------------------------------------------------------------
#############################################################################
##
#O IsQuasigroupTable( ls )
##
## Returns true if <ls> is an n by n latin square with n distinct
## integral entries.
InstallMethod( IsQuasigroupTable, "for matrix",
[ IsMatrix ],
function( ls )
local first_row;
# checking rows
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;
# 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;
end );
#############################################################################
##
#O IsLoopTable( ls )
##
## Returns true if <ls> is a normalized latin square. An n by n latin square
## is normalized if the first row and first column read the same, and the
## entries in the first row are ordered.
InstallMethod( IsLoopTable, "for matrix",
[ IsMatrix ],
function( ls )
if not IsQuasigroupTable( ls ) then return false; fi;
return Set( ls[ 1 ] ) = ls[ 1 ]
and ls[ 1 ] = List( [1..Length(ls)], i -> ls[ i ][ 1 ] );
end );
#############################################################################
##
#O CanonicalCayleyTable( ls )
##
## Returns a Cayley table isomorphic to <ls>, 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.
InstallMethod( CanonicalCayleyTable, "for matrix",
[ IsMatrix ],
function( ls )
local n, entries, i, j, T;
n := Length( ls );
# finding all distinct entries in the table
entries := [];
for i in [1..n] do for j in [1..n] do
AddSet( entries, ls[ i ][ j ] );
od; od;
# 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 NormalizedQuasigroupTable( ls )
##
## Given a latin square <ls>, returns the corresponding normalized
## latin square with entries 1, ..., n.
InstallMethod( NormalizedQuasigroupTable, "for matrix",
[ IsMatrix ],
function( ls )
local T, perm;
if not IsQuasigroupTable( ls ) then
Error( "LOOPS: <1> must be a latin square." );
fi;
# renaming the entries to be 1, ..., n
T := CanonicalCayleyTable( ls );
# permuting the columns so that the first row reads 1, ..., n
perm := PermList( T[ 1 ] );
T := List( T, row -> Permuted( row, perm ) );
# permuting the rows so that the first column reads 1, ..., n
return Set( T );
end );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS MANUALLY
## -------------------------------------------------------------------------
#############################################################################
##
#A CayleyTable( Q )
##
## Returns the Cayley table of the quasigroup <Q>
InstallMethod( CayleyTable, "for quasigroup",
[ IsQuasigroup ],
function( Q )
local elms, parent_elms;
elms := Elements( Q );
parent_elms := Elements( Parent( Q ) );
return List( elms, x-> List( elms, y -> Position( parent_elms, x * y ) ) );
end );
#############################################################################
##
#O QuasigroupByCayleyTable( ct )
##
## Returns quasigroup with multiplication table <ct>.
InstallMethod( QuasigroupByCayleyTable, "for matrix",
[ IsMatrix ],
function( ct )
local F, Q, elms, n;
if not IsQuasigroupTable( ct ) then
Error( "LOOPS: <1> must be a latin square." );
fi;
# Making sure that entries are 1, ..., n
ct := CanonicalCayleyTable( ct );
# constructing the family
F := NewFamily( "QuasigroupByCayleyTableFam", IsQuasigroupElement );
# installing data for the family
n := Length ( ct );
F!.size := n;
elms := Immutable( List( [1..n], i -> Objectify(
NewType( F, IsQuasigroupElement and IsQuasigroupElmRep), [ i ] ) ) );
F!.set := elms;
F!.cayleyTable := ct;
F!.names := "q";
# creating the quasigroup
Q := Objectify( NewType( FamilyObj( elms ),
IsQuasigroup and IsAttributeStoringRep ), rec() );
# setting attributes for the quasigroup
SetSize( Q, n );
SetAsSSortedList( Q, elms );
SetParent( Q, Q );
SetCayleyTable( Q, ct );
return Q;
end );
#############################################################################
##
#O LoopByCayleyTable( ct )
##
## Returns loop with multiplication table <ct>.
InstallMethod( LoopByCayleyTable, "for matrix",
[ IsMatrix ],
function( ct )
local F, L, elms, n;
if not IsLoopTable( ct ) then
Error( "LOOPS: <1> must be a normalized latin square." );
fi;
# Making sure that the entries are 1, ..., n.
# The table will remain normalized.
ct := CanonicalCayleyTable( ct );
# constructing the family
F := NewFamily( "LoopByCayleyTableFam", IsLoopElement );
# installing the data for the family
n := Length ( ct );
F!.size := n;
elms := Immutable( List( [1..n], i -> Objectify(
NewType( F, IsLoopElement and IsLoopElmRep), [ i ] ) ) );
F!.set := elms;
F!.cayleyTable := ct;
F!.names := "l";
# creating the loop
L := Objectify( NewType( FamilyObj( elms ),
IsLoop and IsAttributeStoringRep ), rec() );
# setting attributes for the loop
SetSize( L, n );
SetAsSSortedList( L, elms );
SetParent( L, L );
SetCayleyTable( L, ct );
SetOne( L, elms[ 1 ] );
return L;
end );
#############################################################################
##
#O SetQuasigroupElmName( Q, name )
##
## Changes the name of elements of quasigroup or loop <Q> to <name>
InstallMethod( SetQuasigroupElmName, "for quasigroup and string",
[ IsQuasigroup, IsString ],
function( Q, name )
local F;
F := FamilyObj( Elements( Q )[ 1 ] );
F!.names := name;
end);
#############################################################################
##
#O CanonicalCopy( Q )
##
## Returns a canonical copy of <Q>, that is, an isomorphic object <O> with
## canonical multiplication table and Parent( <O> ) = <O>.
## (PROG) Properties and attributes are lost!
InstallMethod( CanonicalCopy, "for quasigroup or loop",
[ IsQuasigroup ],
function( Q )
if IsLoop( Q ) then
return LoopByCayleyTable( CayleyTable( Q ) );
fi;
return QuasigroupByCayleyTable( CayleyTable( Q ) );
end);
#############################################################################
## CREATING QUASIGROUPS AND LOOPS FROM A FILE
## -------------------------------------------------------------------------
#############################################################################
##
#F LOOPS_ReadCayleyTableFromFile( filename, replace_by_spaces )
##
## Auxiliary function. Reads the content of <filename> and tries to
## interpret the data as a multiplication table, according to the rules
## summarized below. If successful, it returns the multiplication table as
## an n by n array.
## ALGORITHM:
## 1) the content of the file is read into one string
## 2) all end-of-lines and all characters in the string <replace_by_spaces>
## are replaced by spaces
## 3) the string is split into chunks, where chunk = maximal substring
## without spaces
## 4) the number n of distinct chunks is found. If the number of chunks
## is not n^2, error is announced and function terminates
## 5) a numerical value 1 .. n is assigned to each chunk, depending on
## its position among the distinct chunks
## 6) multiplication table is constructed and returned
InstallGlobalFunction( LOOPS_ReadCayleyTableFromFile,
function( filename, replace_by_spaces)
local input, s, i, chunks, started, starting_pos, z, j, distinct_chunks, c, n, T;
if not ( IsString( filename) and IsString( replace_by_spaces ) ) then
Error( "LOOPS: <1> must be a file name, and <2> must be a string." );
fi;
input := InputTextFile( filename );
if input = fail then Error( "LOOPS: <1> is not a valid file name." ); fi;
s := ReadAll( input );
# removing end-of-lines, etc.
for i in [1..Length( s )] do
if (s[ i ] = '\n') or (s[ i ] in replace_by_spaces) then
s[ i ] := ' ';
fi;
od;
s[ Length( s ) + 1 ] := ' '; #to make sure that string ends with space
#parsing string into chunks separated by spaces
chunks := [];
started := false; starting_pos := 0;
for i in [1..Length( s )] do
if not started then
if not s[ i ] = ' ' then
started := true;
starting_pos := i;
fi;
else
if s[ i ] = ' ' then #end of chunk
z := "";
for j in [ starting_pos..i-1 ] do
z[ j - starting_pos + 1 ] := s[ j ];
od;
Add( chunks, z );
started := false;
fi;
fi;
od;
distinct_chunks := [];
for c in chunks do if not c in distinct_chunks then
Add( distinct_chunks, c );
fi; od;
n := Length( distinct_chunks );
if not Length( chunks ) = n^2 then
Error( "LOOPS: The data in the file cannot be arranged into a square table." );
fi;
T := List( [1..n], i -> 0*[1..n] );
for i in [1..n] do for j in [1..n] do
T[ i ][ j ] := Position( distinct_chunks, chunks[ (i-1)*n + j ] );
od; od;
return T;
end);
#############################################################################
##
#O QuasigroupFromFile( filename, replace] )
##
## Calls LOOPS_ReadCayleyTableFromFile( filename, replace ) in order to return
## the quasigroup with multiplication table in file <filename>.
InstallMethod( QuasigroupFromFile, "for string and string",
[ IsString, IsString ],
function( filename, replace )
return QuasigroupByCayleyTable( LOOPS_ReadCayleyTableFromFile( filename, replace ) );
end );
#############################################################################
##
#O LoopFromFile( filename , replace] )
##
## Calls LOOPS_ReadCayleyTableFromFile( filename, replace ) in order to return
## the loop with multiplication table in file <filename>.
InstallMethod( LoopFromFile, "for string and string",
[ IsString, IsString ],
function( filename, replace )
return LoopByCayleyTable( LOOPS_ReadCayleyTableFromFile( filename, replace ) );
end );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS AND FOLDERS
## -------------------------------------------------------------------------
#############################################################################
##
#O CayleyTableByPerms( perms )
##
## Given a set <perms> of n permutations of an n-element set X, returns
## n by n Cayley table ct such that ct[i][j] = X[j]^perms[i].
## The operation is safe only if at most one permutation of <perms> is
## the identity permutation, and all other permutations of <perms>
## move all points of X.
InstallMethod( CayleyTableByPerms,
"for a list of permutations",
[ IsPermCollection ],
function( perms )
local n, pts, max;
n := Length( perms );
if n=1 then
return [ [ 1 ] ];
fi;
# one of perms[ 1 ], perms[ 2 ] must move all points
pts := MovedPoints( perms[ 2 ] );
if pts = [] then
pts := MovedPoints( perms[ 1 ] );
fi;
max := Maximum( pts );
# 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 } );
end);
#############################################################################
##
#O QuasigroupByLeftSection( sect )
##
## Returns the quasigroup whose left section is the list of permutations
## <sect>.
InstallMethod( QuasigroupByLeftSection,
"for a set of left translation maps",
[ IsPermCollection ],
function( sect )
return QuasigroupByCayleyTable( CayleyTableByPerms( sect ) );
end);
#############################################################################
##
#O LoopByLeftSection( sect )
##
## Returns the loop whose left section is the list of permutations <sect>.
## Since the order of translations in <sect> is determined by their
## image of the neutral element 1, we disregard the order.
InstallMethod( LoopByLeftSection,
"for a set of left translation maps",
[ IsPermCollection ],
function( sect )
return LoopByCayleyTable( Set ( CayleyTableByPerms( sect ) ) );
end);
#############################################################################
##
#O QuasigroupByRightSection( sect )
##
## Returns the quasigroup whose right section is the list of permutations
## <sect>.
InstallMethod( QuasigroupByRightSection,
"for a set of left translation maps",
[ IsPermCollection ],
function( sect )
return QuasigroupByCayleyTable( TransposedMat ( CayleyTableByPerms( sect ) ) );
end);
#############################################################################
##
#O LoopByRightSection( sect )
##
## Returns the loop whose right section is the list of permutations <sect>.
## Since the order of translations in <sect> is determined by their
## image of the neutral element 1, we disregard the order.
InstallMethod( LoopByRightSection,
"for a set of left translation maps",
[ IsPermCollection ],
function( sect )
return LoopByCayleyTable( TransposedMat ( Set ( CayleyTableByPerms( sect ) ) ) );
end);
#############################################################################
##
#O LOOPS_CayleyTableByRightFolder( G, H, T )
##
## Auxiliary operation.
##
## A right folder is a triple (G,H,T) such that G is a group, H is
## a subgroup of G, and T is a right transversal to H in G.
##
## Returns the multiplication table on {Hx: x in G} by Ht*Hs = H(ts).
##
## The multiplication table is a quasigroup if and only if
## T is a right transversal to every conjugate H^g in G.
InstallGlobalFunction( LOOPS_CayleyTableByRightFolder,
function( G, H, T )
local act, nT, actT, i, p, ct;
# act = action of G on right cosest G/H
act := ActionHomomorphism( G, RightCosets( G, H ), OnRight );
nT := Length( T );
# actT = permutations on G/H induced by elements of T
actT := [1..nT];
for i in [1..nT] do
actT[ i ] := T[ i ]^act;
od;
# the order of right cosets determined by T might not agree with the default order of right cosets ...
p := PermList( List( [1..nT], i -> 1^actT[ i ] ) );
ct := List( [1..nT], i -> ListPerm( p * actT[ i ] * p^(-1) ) );
for i in [1..nT] do
if ct[ i ] = [] then # this can happen since ListPerm( () ) = []
ct[ i ] := [1..nT];
fi;
od;
return TransposedMat( ct );
end);
#############################################################################
##
#O QuasigroupByRightFolder( G, H, T )
##
## See CayleyTableByRightFolder. We do not check if the right folder
## is a quasigroup right folder.
InstallMethod( QuasigroupByRightFolder,
"for a group, a subgroup and right transversal",
[ IsGroup, IsGroup, IsMultiplicativeElementCollection ],
function( G, H, T )
return QuasigroupByCayleyTable( LOOPS_CayleyTableByRightFolder( G, H, T ) );
end);
#############################################################################
##
#O LoopByRightFolder( G, H, T )
##
## See CayleyTableByRigthFolder. We do not check if the right folder
## is a loop right folder.
InstallOtherMethod( LoopByRightFolder,
"for a group, a subgroup and right transversal",
[ IsGroup, IsGroup, IsMultiplicativeElementCollection ],
function( G, H, T )
return LoopByCayleyTable( LOOPS_CayleyTableByRightFolder( G, H, T ) );
end);
#############################################################################
## CONVERSIONS
## -------------------------------------------------------------------------
#############################################################################
##
#O IntoQuasigroup( M )
##
## Given a magma, returns the corresponding quasigroup, if possible.
InstallMethod( IntoQuasigroup, "for magma",
[ IsMagma ],
function( M )
local ct;
if IsQuasigroup( M ) then # leave quasigroups and loops intact
return M;
fi;
# magma, not necessarily a quasigroup
ct := MultiplicationTable( Elements( M ) );
if IsQuasigroupTable( ct ) then
return QuasigroupByCayleyTable( ct );
fi;
return fail;
end);
#############################################################################
##
#O PrincipalLoopIsotope( Q, f, g )
##
## Let Q be a quasigroup and f, g elements of Q.
## Define new operation on Q by x+y = R^{-1}(g)(x) * L^{-1}(f)(y).
## Then (Q,+) is a loop with neutral element f*g.
## We return isomorphic copy of (Q,+) via the isomorphism (1,f*g).
InstallMethod( PrincipalLoopIsotope,
"for quasigroup and two quasigroup elements",
[ IsQuasigroup, IsQuasigroupElement, IsQuasigroupElement ],
function( Q, f, g )
local n, L, R, i, j, ct, p;
if not (f in Q and g in Q) then
Error("LOOPS: <2> and <3> must be elements of quasigroup <1>.");
fi;
# constructing new multiplication
n := Size( Q );
ct := List( [1..n], i -> [1..n] );
L := Inverse( LeftTranslation( Q, f ) ); # inverse of left translation by f
R := Inverse( RightTranslation( Q, g ) ); # inverse or right translation by g
for i in [1..n] do for j in [1..n] do
ct[ i ][ j ] := CayleyTable( Q )[ i^R ][ j^L ];
od; od;
# the neutral element of ct is now f*g. We apply isomorphism (1, f*g).
p := Position(Q, f*g);
if p>1 then
p := (1, p); # note that p is its own inverse
ct := List([1..n], i-> List([1..n], j -> ( ct[ i^p ][ j^p ] )^p ) );
fi;
return LoopByCayleyTable( ct );
end);
#############################################################################
##
#O IntoLoop( M )
##
## Given a magma, returns the corresponding loop, if possible.
InstallMethod( IntoLoop, "for magma",
[ IsMagma ],
function( M )
local e, p, ct;
if IsLoop( M ) then # loops are left intact
return M;
fi;
# magma, not necessarily a loop
M := IntoQuasigroup( M );
if M = fail then
return fail;
fi;
# quasigroup, not necesarily a loop
e := MultiplicativeNeutralElement( M );
if e = fail then # no neutral element, use principal isotope
return PrincipalLoopIsotope( M, Elements( M )[ 1 ], Elements( M )[ 1 ] );
fi;
# quasigroup with neutral element, i.e., a loop
p := Position( M, e );
if p>1 then
p := (1,p); # note that p is its own inverse
ct := List([1..Size(M)], i-> List([1..Size(M)], j ->
( CayleyTable( M )[ i^p ][ j^p ] )^p
) );
else
ct := CayleyTable( M );
fi;
return LoopByCayleyTable( ct );
end );
#############################################################################
##
#O IntoGroup( M )
##
## Given a magma <M>, returns the corresponding group, if possible.
InstallOtherMethod( IntoGroup, "for magma",
[ IsMagma ],
function( M )
if IsGroup( M ) then # groups are left intact
return M;
fi;
# magma, not necessarily a group
M := IntoLoop( M );
if M=fail or (not IsAssociative( M ) ) then
return fail;
fi;
# group
return RightMultiplicationGroup( M );
end);
#############################################################################
## PRODUCTS OF QUASIGROUPS AND LOOPS
## -------------------------------------------------------------------------
#############################################################################
##
#F DirectProduct( Q1, Q2, ..., Qn )
##
## Returns the direct product of quasigroups <Q1>, <Q2>, ... , <Qn>.
## The quasigroups can be declared as quasigroups, loops or groups.
# The following is necessary due to implementation of DirectProduct for
# 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.
# However, we do not know if such a list will be processed with
# DirectProductOp( <IsList>, <IsGroup> ), or
# DirectProductOp( <IsList>, <IsQuasigroup> ),
# since this depends on which algebra is listed first.
# We therefore take care of both situations.
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsGroup> )",
[ 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();
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;
fi;
od;
return DirectProductOp( list, list[ 1 ] );
end);
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )",
[ 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;
# 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();
fi;
# 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 ) );
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
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 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
return IntoLoop( quasigroup_list[1] );
fi;
return quasigroup_list[ 1 ];
end );
#############################################################################
## OPPOSITE QUASIGROUPS AND LOOPS
## --------------------------------------------------------------------------
#############################################################################
##
#O OppositeQuasigroup( Q )
##
## Returns the quasigroup opposite to the quasigroup <Q>.
InstallMethod( OppositeQuasigroup, "for quasigroup",
[ IsQuasigroup ],
function( Q )
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
#############################################################################
##
#O OppositeLoop( Q )
##
## Returns the loop opposite to the loop <Q>.
InstallMethod( OppositeLoop, "for loop",
[ IsLoop ],
function( Q )
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
#############################################################################
##
#A Opposite( Q )
##
## Returns the quasigroup opposite to the quasigroup <Q>. When
## <Q> is a loop, a loop is returned.
InstallMethod( Opposite, "for quasigroup",
[ IsQuasigroup ],
function( Q )
if IsLoop( Q ) then
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
fi;
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
end );
#############################################################################
## DISPLAYING QUASIGROUPS AND LOOPS
## -------------------------------------------------------------------------
InstallMethod( ViewObj, "for quasigroup",
[ IsQuasigroup ],
function( Q )
Print( "<quasigroup of order ", Size( Q ), ">" );
end );
## dangerous for large quasigroups
InstallMethod( PrintObj, "for quasigroup",
[ IsQuasigroup ],
function( Q )
if HasCayleyTable( Q ) then
Print( "<quasigroup with multiplication table\n" );
PrintArray( CayleyTable( Q ) );
else
Print( "<quasigroup with elements\n" );
Print( Elements( Q ) );
fi;
Print( ">\n" );
end );
InstallMethod( ViewObj, "for loop",
[ IsLoop ],
function( L )
if HasIsAssociative( L ) and IsAssociative( L ) then
Print( "<associative loop of order ", Size( L ), ">");
elif HasIsExtraLoop( L ) and IsExtraLoop( L ) then
Print( "<extra loop of order ", Size( L ), ">");
elif HasIsMoufangLoop( L ) and IsMoufangLoop( L ) then
Print( "<Moufang loop of order ", Size( L ), ">");
elif HasIsCLoop( L ) and IsCLoop( L ) then
Print( "<C loop of order ", Size( L ), ">");
elif HasIsLeftBolLoop( L ) and IsLeftBolLoop( L ) then
Print( "<left Bol loop of order ", Size( L ), ">");
elif HasIsRightBolLoop( L ) and IsRightBolLoop( L ) then
Print( "<right Bol loop of order ", Size( L ), ">");
elif HasIsLCLoop( L ) and IsLCLoop( L ) then
Print( "<LC loop of order ", Size( L ), ">");
elif HasIsRCLoop( L ) and IsRCLoop( L ) then
Print( "<RC loop of order ", Size( L ), ">");
elif HasIsLeftAlternative( L ) and IsLeftAlternative( L ) then
if HasIsRightAlternative( L ) and IsRightAlternative( L ) then
Print( "<alternative loop of order ", Size( L ), ">");
else
Print( "<left alternative loop of order ", Size( L ), ">");
fi;
elif HasIsRightAlternative( L ) and IsRightAlternative( L ) then
Print( "<right alternative loop of order ", Size( L ), ">");
elif HasIsFlexible( L ) and IsFlexible( L ) then
Print( "<flexible loop of order ", Size( L ), ">");
else
# MORE ??
Print( "<loop of order ", Size( L ), ">" );
fi;
end );
## dangerous for large loops
InstallMethod( PrintObj, "for loop",
[ IsLoop ],
function( L )
if HasCayleyTable( L ) then
Print( "<loop with multiplication table\n" );
PrintArray( CayleyTable( L ) );
else
Print( "<loop with elements\n" );
Print( Elements( L ) );
fi;
Print( ">\n" );
end );

17
gap/random.gd Normal file
View file

@ -0,0 +1,17 @@
############################################################################
##
#W random.gd Random loops [loops]
##
#H @(#)$Id: random.gd, v 2.1.0 2008/12/08 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
DeclareOperation( "RandomQuasigroup", [IsInt, IsInt ] );
# There is also a version of RandomLoop with [ IsInt ].
DeclareOperation( "RandomLoop", [ IsInt, IsInt ] );
# There is also a version of RandomLoop with [ IsInt ].
DeclareOperation( "RandomNilpotentLoop", [ IsList ] );

208
gap/random.gi Normal file
View file

@ -0,0 +1,208 @@
#############################################################################
##
#W random.gi Random loops [loops]
##
#H @(#)$Id: random.gi, v 2.1.0 2008/12/08 gap Exp $
##
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
#Y P. Vojtechovsky (University of Denver, USA)
##
#############################################################################
##
#O RandomQuasigroup( n, iter )
##
## Returns a random quasigroup of order <n> using <iter> random steps to move into an
## initial position in the Jacobson & Matthews method.
## (MATH) This is an implementation of the Jacobson & Matthews random walk method with
## some ad hoc mixing parameters. We always start with the cyclic group of order n.
## It is proved in Jacobson & Matthews that a random walk in the graph visits all
## latin squares uniformly. (But the problem is how to move to an initial position.)
InstallMethod( RandomQuasigroup, "for two integers",
[ IsInt, IsInt ],
function( n, iter )
local f, x, y, z, is_proper, xx, yy, zz, random_walk_step, i, ct;
if not ( n > 0 and iter > 0 ) then
Error("LOOPS: the arguments must be positive integers.");
fi;
# n=1 is a special case
if n=1 then
return LoopByCayleyTable( [[1]] );
fi;
# initializing funciton for proper and improper Latin squares
# the meaning of f(x,y,z)=1 is that there is symbol z in row x and column y
f := List([1..n], i -> List([1..n], j -> 0*[1..n]));
# cyclic group of order n on symbols [1..n]
for x in [1..n] do for y in [1..n] do
z := x+y-1;
if z > n then
z := z - n;
fi;
f[x][y][z] := 1;
od; od;
is_proper := true; # proper latin square to start with
# one random walk step
random_walk_step := function()
local x, y, z, triples, x2, y2, z2, triple;
if is_proper then
repeat
x := Random([1..n]);
y := Random([1..n]);
z := Random([1..n]);
until f[x][y][z]=0;
fi;
if not is_proper then # use unique point with f(x,y,z)=-1
x := xx;
y := yy;
z := zz;
fi;
# find all suitable triples
x2 := Filtered( [1..n], a -> f[a][y][z] = 1 );
y2 := Filtered( [1..n], a -> f[x][a][z] = 1 );
z2 := Filtered( [1..n], a -> f[x][y][a] = 1 );
# pick a random suitable triple
x2 := Random( x2 );
y2 := Random( y2 );
z2 := Random( z2 );
# shuffle values
f[x][y][z] := f[x][y][z] + 1;
f[x][y2][z2] := f[x][y2][z2] + 1;
f[x2][y][z2] := f[x2][y][z2] + 1;
f[x2][y2][z] := f[x2][y2][z] + 1;
f[x2][y][z] := f[x2][y][z] - 1;
f[x][y2][z] := f[x][y2][z] - 1;
f[x][y][z2] := f[x][y][z2] - 1;
f[x2][y2][z2] := f[x2][y2][z2] - 1;
# determine properness
if f[x2][y2][z2] = 0 then
is_proper := true;
else
is_proper := false;
xx := x2;
yy := y2;
zz := z2;
fi;
end;
# moving into an initial point in the graph
for i in [1..iter] do
random_walk_step();
od;
# finding a proper square nearby
while not is_proper do
random_walk_step();
od;
# constructing the multiplication table from the function
ct := List([1..n], i->[1..n]);
for x in [1..n] do for y in [1..n] do
ct[x][y] := Filtered([1..n], z -> f[x][y][z] = 1)[ 1 ];
od; od;
return QuasigroupByCayleyTable( ct );
end);
#############################################################################
##
#O RandomQuasigroup( n )
##
## Returns random quasigroup of order <n> using n^3 steps to move into an
## initial position in the Jacobson & Matthews algorithm.
InstallOtherMethod( RandomQuasigroup, "for a positive integer",
[ IsInt ],
function( n )
return RandomQuasigroup( n, n^3 );
end);
#############################################################################
##
#O RandomLoop( n, iter )
##
## Returns a normalized random quasigroup of order <n>.
InstallOtherMethod( RandomLoop, "for two positive integers",
[ IsInt, IsInt ],
function( n, iter )
local Q;
Q := RandomQuasigroup( n, iter );
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
end);
#############################################################################
##
#O RandomLoop( n )
##
## Returns random loop of order <n> using n^3 steps to move into an
## initial position in the Jacobson & Matthews algorithm.
InstallOtherMethod( RandomLoop, "for a positive integer",
[ IsInt ],
function( n )
local Q;
Q := RandomQuasigroup( n );
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
end);
#############################################################################
##
#O RandomNilpotentLoop( lst )
##
## lst must be a list of positive integers and/or finite abelian groups.
## If lst = [n] and n is an integer, returns a random abelian group of order n.
## If lst = [A] and A is an abelian group, returns AsLoop( A ).
## If lst = [a1,..,am] and a1 is an integer, returns a central extension
## of an abelian group of order a1 by RandomNilpotentLoop( [a2,...,am] ).
## If lst = [a1,..,am] and a1 is a group, returns a central extension
## of a1 by RandomNilpotentLoop( [a2,...,am] ).
## To determine the nilpotency class CL of the resulting loop, assume that
## lst has length at least 2, contains only integers bigger than 1 (the "1" entries are trivial),
## and let m be the last entry of lst. If m>2 then CL=Length(lst), else CL = Length(lst)-1.
InstallMethod( RandomNilpotentLoop, "for a list of abelian groups and positive integers",
[ IsList ],
function( lst )
local n, K, F, f, theta, i, j, phi;
if IsEmpty( lst ) then
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
fi;
n := lst[1];
if not ( IsPosInt( n ) or (IsGroup( n ) and IsAbelian( n ) and IsFinite( n ) ) ) then
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
fi;
# central subloop
if IsInt( n ) then # first argument is a positive integer
K := IntoLoop( Random( AllGroups( n, IsAbelian ) ) );
else # first argument is an abelian group
K := IntoLoop( n );
fi;
# factor loop
if Length( lst ) = 1 then # trivial factor
F := LoopByCayleyTable( [ [ 1 ] ] );
else
F := RandomNilpotentLoop( lst{[2..Length(lst)]} );
fi;
# cocycle (random)
f := Size( F );
theta := List([1..f], i->[1..f]);
for i in [2..f] do
theta[1][i]:=1;
od;
for i in [2..f] do for j in [2..f] do
theta[i][j] := Random( [1..Size(K)] );
od; od;
# To guarantee that the resulting loop has maximal nilpotency class,
# it suffices to make sure that theta is not symmetric.
if f>2 then
i := Random([2..f]);
j := Random( Difference( [2..f], [i] ) );
theta[i][j] := Random( Difference( [1..Size(K)], [ theta[j][i] ] ) );
fi;
# trivial action
phi := List( [1..f], i->() );
# the loop
return LoopByExtension( K, F, phi, theta );
end);