GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W cong.gi The Congruence package Ann Dooms
#W Eric Jespers
#W Alexander Konovalov
##
##
#############################################################################
#############################################################################
##
## Constructors of congruence subgroups
InstallMethod( PrincipalCongruenceSubgroup, "for positive integer",
[ IsPosInt ],
function(n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, true,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, false,
IsCongruenceSubgroupGammaMN, false );
return G;
end);
InstallMethod( CongruenceSubgroupGamma0, "for positive integer",
[ IsPosInt ],
function(n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, true, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, false,
IsCongruenceSubgroupGammaMN, false );
return G;
end);
InstallMethod( CongruenceSubgroupGammaUpper0, "for positive integer",
[ IsPosInt ],
function(n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, true,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, false,
IsCongruenceSubgroupGammaMN, false );
return G;
end);
InstallMethod( CongruenceSubgroupGamma1, "for positive integer",
[ IsPosInt ],
function(n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, true, IsCongruenceSubgroupGammaUpper1, false,
IsCongruenceSubgroupGammaMN, false );
return G;
end);
InstallMethod( CongruenceSubgroupGammaUpper1, "for positive integer",
[ IsPosInt ],
function(n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, true,
IsCongruenceSubgroupGammaMN, false );
return G;
end);
InstallMethod( CongruenceSubgroupGammaMN, "for two positive integers",
[ IsPosInt, IsPosInt ],
function(m,n)
local type, G;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, m*n,
LevelOfCongruenceSubgroupGammaMN, [m,n],
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, false,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, false,
IsCongruenceSubgroupGammaMN, true );
return G;
end);
InstallGlobalFunction( IntersectionOfCongruenceSubgroups,
function( arg )
local type, G, H, K, T, arglist, n, i, pos;
type := NewType( FamilyObj([[[1,0],[0,1]]]),
IsGroup and
IsAttributeStoringRep and
IsFinitelyGeneratedGroup and
IsMatrixGroup and
IsCongruenceSubgroup);
if not ForAll( arg, IsCongruenceSubgroup ) then
Error("Usage : IntersectionOfCongruenceSubgroups( G1, G2, ... GN ) \n");
fi;
# First we create a list arglist to eliminate evident repetitions of subgroups.
# Then we eliminate evident inclusions of one subgroup into another:
# - since intersection is associative, if we can intersect the group T which
# is to be added with another subgroup K already contained in alglist, and
# the result is one of the canonical congruence subgroups, we replace K by
# the result of intersection of K and T
# - we do not add a subgroup T to the list of defining subgroups, if alglist
# already contains another subgroup K such that K is in T.
# - if we add to alglist a subgroup T and alglist already contains one or more
# subgroups K such that T is in K, we add T and remove all these K.
arglist := [];
for H in arg do
if IsIntersectionOfCongruenceSubgroups(H) then
for T in DefiningCongruenceSubgroups( H ) do
pos:=PositionProperty( arglist, K ->
CanReduceIntersectionOfCongruenceSubgroups( K, T ) );
if pos<>fail then
arglist[pos]:=Intersection( arglist[pos], T );
else
if ForAll( arglist, K -> not CanEasilyCompareCongruenceSubgroups( K, T ) ) and
ForAll( arglist, K -> not IsSubgroup( T, K ) ) then
for i in [ 1 .. Length(arglist) ] do
if IsSubgroup( arglist[i], T ) then
Unbind( arglist[i] );
fi;
od;
arglist := Compacted( arglist );
Add( arglist, T );
fi;
fi;
od;
else
pos:=PositionProperty( arglist, K ->
CanReduceIntersectionOfCongruenceSubgroups( K, H ) );
if pos<>fail then
arglist[pos]:=Intersection( arglist[pos], H );
else
if ForAll( arglist, K -> not CanEasilyCompareCongruenceSubgroups( K, H ) ) and
ForAll( arglist, K -> not IsSubgroup( H, K ) ) then
for i in [ 1 .. Length(arglist) ] do
if IsSubgroup( arglist[i], H ) then
Unbind( arglist[i] );
fi;
od;
arglist := Compacted( arglist );
Add( arglist, H );
fi;
fi;
fi;
od;
# if the list of defining subgroups was reduced
# to a single subgroup, we return this subgroup
if Length( arglist ) = 1 then
return arglist[1];
fi;
# otherwise we sort the list of defining subgroups:
# types of subgroups are sorted in the following way:
# - IsCongruenceSubgroupGamma0
# - IsCongruenceSubgroupGammaUpper0
# - IsCongruenceSubgroupGamma1
# - IsCongruenceSubgroupGammaUpper1
# - IsPrincipalCongruenceSubgroup
# and subgroups of the same type are sorted by ascending level
Sort( arglist,
function(X,Y)
local f, t;
f:=[IsCongruenceSubgroupGamma0,IsCongruenceSubgroupGammaUpper0,IsCongruenceSubgroupGamma1,IsCongruenceSubgroupGammaUpper1,IsPrincipalCongruenceSubgroup];
return PositionProperty(f, t -> t(X)) < PositionProperty(f, t -> t(Y)) or
( PositionProperty(f, t -> t(X)) = PositionProperty(f, t -> t(Y)) and
LevelOfCongruenceSubgroup(X) < LevelOfCongruenceSubgroup(Y) );
end );
n := Lcm( List( arglist, H -> LevelOfCongruenceSubgroup(H) ) );
G := rec();
ObjectifyWithAttributes( G, type,
DimensionOfMatrixGroup, 2,
OneImmutable, [[1,0],[0,1]],
IsIntegerMatrixGroup, true,
IsFinite, false,
LevelOfCongruenceSubgroup, n,
IsPrincipalCongruenceSubgroup, false,
IsIntersectionOfCongruenceSubgroups, true,
IsCongruenceSubgroupGamma0, false, IsCongruenceSubgroupGammaUpper0, false,
IsCongruenceSubgroupGamma1, false, IsCongruenceSubgroupGammaUpper1, false,
DefiningCongruenceSubgroups, arglist );
return G;
end);
InstallMethod( DefiningCongruenceSubgroups, "for congruence subgroups",
[ IsCongruenceSubgroup ],
function(G)
if not IsIntersectionOfCongruenceSubgroups(G) then
return [G];
fi;
end);
#############################################################################
##
## Methods for PrintObj and ViewObj for congruence subgroups
InstallMethod( ViewObj,
"for principal congruence subgroup",
[ IsPrincipalCongruenceSubgroup ],
0,
function( G )
Print( "<principal congruence subgroup of level ",
LevelOfCongruenceSubgroup(G), " in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for principal congruence subgroup",
[ IsPrincipalCongruenceSubgroup ],
0,
function( G )
Print( "PrincipalCongruenceSubgroup(",
LevelOfCongruenceSubgroup(G), ")" );
end );
InstallMethod( ViewObj,
"for CongruenceSubgroupGamma0 congruence subgroup",
[ IsCongruenceSubgroupGamma0 ],
0,
function( G )
Print( "<congruence subgroup CongruenceSubgroupGamma_0(",
LevelOfCongruenceSubgroup(G), ") in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for CongruenceSubgroupGamma0 congruence subgroup",
[ IsCongruenceSubgroupGamma0 ],
0,
function( G )
Print( "CongruenceSubgroupGamma0(",
LevelOfCongruenceSubgroup(G), ")" );
end );
InstallMethod( ViewObj,
"for CongruenceSubgroupGammaUpper0 congruence subgroup",
[ IsCongruenceSubgroupGammaUpper0 ],
0,
function( G )
Print( "<congruence subgroup CongruenceSubgroupGamma^0(",
LevelOfCongruenceSubgroup(G), ") in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for CongruenceSubgroupGammaUpper0 congruence subgroup",
[ IsCongruenceSubgroupGammaUpper0 ],
0,
function( G )
Print( "CongruenceSubgroupGammaUpper0(",
LevelOfCongruenceSubgroup(G), ")" );
end );
InstallMethod( ViewObj,
"for CongruenceSubgroupGamma1 congruence subgroup",
[ IsCongruenceSubgroupGamma1 ],
0,
function( G )
Print( "<congruence subgroup CongruenceSubgroupGamma_1(",
LevelOfCongruenceSubgroup(G), ") in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for CongruenceSubgroupGamma1 congruence subgroup",
[ IsCongruenceSubgroupGamma1 ],
0,
function( G )
Print( "CongruenceSubgroupGamma1(",
LevelOfCongruenceSubgroup(G), ")" );
end );
InstallMethod( ViewObj,
"for CongruenceSubgroupGammaUpper1 congruence subgroup",
[ IsCongruenceSubgroupGammaUpper1 ],
0,
function( G )
Print( "<congruence subgroup CongruenceSubgroupGamma^1(",
LevelOfCongruenceSubgroup(G), ") in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for CongruenceSubgroupGammaUpper1 congruence subgroup",
[ IsCongruenceSubgroupGammaUpper1 ],
0,
function( G )
Print( "CongruenceSubgroupGammaUpper1(",
LevelOfCongruenceSubgroup(G), ")" );
end );
InstallMethod( ViewObj,
"for CongruenceSubgroupGammaMN congruence subgroup",
[ IsCongruenceSubgroupGammaMN ],
0,
function( G )
Print( "<congruence subgroup CongruenceSubgroupGammaMN(",
LevelOfCongruenceSubgroupGammaMN(G)[1], ",",
LevelOfCongruenceSubgroupGammaMN(G)[2], ") in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for CongruenceSubgroupGammaMN congruence subgroup",
[ IsCongruenceSubgroupGammaMN ],
0,
function( G )
Print( "CongruenceSubgroupGammaMN(",
LevelOfCongruenceSubgroupGammaMN(G)[1], ",",
LevelOfCongruenceSubgroupGammaMN(G)[2], ")" );
end );
InstallMethod( ViewObj,
"for intersection of congruence subgroups",
[ IsIntersectionOfCongruenceSubgroups ],
0,
function( G )
Print( "<intersection of congruence subgroups of resulting level ",
LevelOfCongruenceSubgroup(G), " in SL_2(Z)>" );
end );
InstallMethod( PrintObj,
"for intersection of congruence subgroups",
[ IsIntersectionOfCongruenceSubgroups ],
0,
function( G )
local i, k;
k := Length(DefiningCongruenceSubgroups(G));
Print( "IntersectionOfCongruenceSubgroups( \n" );
for i in [ 1 .. k-1 ] do
Print( " ", DefiningCongruenceSubgroups(G)[i], ", \n" );
od;
Print( " ", DefiningCongruenceSubgroups(G)[k], " )" );
end );
#############################################################################
##
## Membership tests for congruence subgroups
InstallMethod( \in,
"for a 2x2 matrix and a principal congruence subgroup",
[ IsMatrix, IsPrincipalCongruenceSubgroup],
0,
function( m, G )
local n;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
n := LevelOfCongruenceSubgroup(G);
return IsInt( (m[1][1]-1)/n ) and
IsInt(m[1][2]/n) and
IsInt(m[2][1]/n) and
IsInt( (m[2][2]-1)/n );
fi;
end);
InstallMethod( \in,
"for a 2x2 matrix and a congruence subgroup CongruenceSubgroupGamma0",
[ IsMatrix, IsCongruenceSubgroupGamma0 ],
0,
function( m, G )
local n;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
n := LevelOfCongruenceSubgroup(G);
return IsInt(m[2][1]/n);
fi;
end);
InstallMethod( \in,
"for a 2x2 matrix and a congruence subgroup CongruenceSubgroupGammaUpper0",
[ IsMatrix, IsCongruenceSubgroupGammaUpper0 ],
0,
function( m, G )
local n;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
n := LevelOfCongruenceSubgroup(G);
return IsInt(m[1][2]/n);
fi;
end);
InstallMethod( \in,
"for a 2x2 matrix and a congruence subgroup CongruenceSubgroupGamma1",
[ IsMatrix, IsCongruenceSubgroupGamma1 ],
0,
function( m, G )
local n;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
n := LevelOfCongruenceSubgroup(G);
return IsInt( (m[1][1]-1)/n ) and
IsInt( m[2][1]/n ) and
IsInt( (m[2][2]-1)/n );
fi;
end);
InstallMethod( \in,
"for a 2x2 matrix and a congruence subgroup CongruenceSubgroupGammaUpper1",
[ IsMatrix, IsCongruenceSubgroupGammaUpper1 ],
0,
function( m, G )
local n;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
n := LevelOfCongruenceSubgroup(G);
return IsInt( (m[1][1]-1)/n ) and
IsInt( m[1][2]/n ) and
IsInt( (m[2][2]-1)/n );
fi;
end);
InstallMethod( \in,
"for a 2x2 matrix and a congruence subgroup CongruenceSubgroupGammaMN",
[ IsMatrix, IsCongruenceSubgroupGammaMN ],
0,
function( mat, G )
local m, n;
if not DimensionsMat( mat ) = [2,2] then
return false;
elif DeterminantMat(mat)<>1 then
return false;
else
m := LevelOfCongruenceSubgroupGammaMN(G)[1];
n := LevelOfCongruenceSubgroupGammaMN(G)[2];
return IsInt( (mat[1][1]-1)/m ) and
IsInt(mat[1][2]/m) and
IsInt(mat[2][1]/n) and
IsInt( (mat[2][2]-1)/n );
fi;
end);
InstallMethod( \in,
"for an intersection of congruence subgroups",
[ IsMatrix, IsIntersectionOfCongruenceSubgroups ],
0,
function( m, G )
local H;
if not DimensionsMat( m ) = [2,2] then
return false;
elif DeterminantMat(m)<>1 then
return false;
else
return ForAll( DefiningCongruenceSubgroups(G), H -> m in H );
fi;
end);
#############################################################################
##
## Installing special methods for congruence subgroups
## for some general methods installed in GAP for matrix groups
InstallMethod( DimensionOfMatrixGroup,
"for congruence subgroup",
[ IsCongruenceSubgroup ],
0,
G -> 2 );
InstallMethod( \=,
"for a pair of congruence subgroups",
[ IsCongruenceSubgroup, IsCongruenceSubgroup ],
0,
function( G, H )
if CanEasilyCompareCongruenceSubgroups( G, H ) then
return true;
else
TryNextMethod();
fi;
end);
#############################################################################
##
## IsSubset
##
#############################################################################
InstallMethod( IsSubset,
"for a natural SL_2(Z) and a congruence subgroup",
[ IsNaturalSL, IsCongruenceSubgroup ],
0,
function( G, H )
return MultiplicativeNeutralElement(G)=[ [ 1, 0 ], [ 0, 1 ] ];
end);
InstallMethod( IsSubset,
"for a congruence subgroup and a principal congruence subgroup",
[ IsCongruenceSubgroup, IsPrincipalCongruenceSubgroup ],
0,
function( G, H )
local T;
if IsIntersectionOfCongruenceSubgroups(G) then
return ForAll( DefiningCongruenceSubgroups(G), T -> IsSubset(T,H) );
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGammaUpper1(G) or
IsCongruenceSubgroupGamma0(G) or IsCongruenceSubgroupGammaUpper0(G) then
return IsInt( LevelOfCongruenceSubgroup(H) / LevelOfCongruenceSubgroup(G) );
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
InstallMethod( IsSubset,
"for a congruence subgroup and CongruenceSubgroupGamma1",
[ IsCongruenceSubgroup, IsCongruenceSubgroupGamma1 ],
0,
function( G, H )
local T;
if IsIntersectionOfCongruenceSubgroups(G) then
return ForAll( DefiningCongruenceSubgroups(G), T -> IsSubset(T,H) );
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGammaUpper1(G) or IsCongruenceSubgroupGammaUpper0(G) then
return false;
elif IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGamma0(G) then
return IsInt( LevelOfCongruenceSubgroup(H) / LevelOfCongruenceSubgroup(G) );
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
InstallMethod( IsSubset,
"for a congruence subgroup and CongruenceSubgroupGammaUpper1",
[ IsCongruenceSubgroup, IsCongruenceSubgroupGammaUpper1 ],
0,
function( G, H )
local T;
if IsIntersectionOfCongruenceSubgroups(G) then
return ForAll( DefiningCongruenceSubgroups(G), T -> IsSubset(T,H) );
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGamma0(G) then
return false;
elif IsCongruenceSubgroupGammaUpper1(G) or IsCongruenceSubgroupGammaUpper0(G) then
return IsInt( LevelOfCongruenceSubgroup(H) / LevelOfCongruenceSubgroup(G) );
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
InstallMethod( IsSubset,
"for a congruence subgroup and CongruenceSubgroupGamma0",
[ IsCongruenceSubgroup, IsCongruenceSubgroupGamma0 ],
0,
function( G, H )
local T;
if IsIntersectionOfCongruenceSubgroups(G) then
return ForAll( DefiningCongruenceSubgroups(G), T -> IsSubset(T,H) );
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGammaUpper1(G) or IsCongruenceSubgroupGammaUpper0(G) then
return false;
elif IsCongruenceSubgroupGamma0(G) then
return IsInt( LevelOfCongruenceSubgroup(H) / LevelOfCongruenceSubgroup(G) );
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
InstallMethod( IsSubset,
"for a congruence subgroup and CongruenceSubgroupGammaUpper0",
[ IsCongruenceSubgroup, IsCongruenceSubgroupGammaUpper0 ],
0,
function( G, H )
local T;
if IsIntersectionOfCongruenceSubgroups(G) then
return ForAll( DefiningCongruenceSubgroups(G), T -> IsSubset(T,H) );
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGammaUpper1(G) or IsCongruenceSubgroupGamma0(G) then
return false;
elif IsCongruenceSubgroupGammaUpper0(G) then
return IsInt( LevelOfCongruenceSubgroup(H) / LevelOfCongruenceSubgroup(G) );
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
InstallMethod( IsSubset,
"for a congruence subgroup and intersection of congruence subgroups",
[ IsCongruenceSubgroup, IsIntersectionOfCongruenceSubgroups ],
0,
function( G, H )
local DG, DH;
# here we can check only sufficient conditions, and they are not
# satisfied, then we call the next method
if IsIntersectionOfCongruenceSubgroups(G) then
if ForAll( DefiningCongruenceSubgroups(H), DH ->
ForAll( DefiningCongruenceSubgroups(G), DG ->
IsSubset(G,DH) ) ) then
return true;
else
TryNextMethod();
fi;
elif IsPrincipalCongruenceSubgroup(G) or
IsCongruenceSubgroupGamma1(G) or IsCongruenceSubgroupGammaUpper1(G) or
IsCongruenceSubgroupGamma0(G) or IsCongruenceSubgroupGammaUpper0(G) then
if ForAll( DefiningCongruenceSubgroups(H), DH -> IsSubset(G,DH) ) then
return true;
else
TryNextMethod();
fi;
else
# for a case of another type of congruence subgroup
TryNextMethod();
fi;
end);
#############################################################################
##
## Intersection2
##
#############################################################################
InstallMethod( Intersection2,
"for a pair of congruence subgroups",
[ IsCongruenceSubgroup, IsCongruenceSubgroup ],
0,
function( G, H )
#
# Case 1 - at least one subgroup is an intersection of congruence subgroups
#
if IsIntersectionOfCongruenceSubgroups(G) or
IsIntersectionOfCongruenceSubgroups(H) then
return IntersectionOfCongruenceSubgroups(G,H);
#
# Case 2 - the diagonal (both subgroups has the same type)
#
elif IsPrincipalCongruenceSubgroup(G) and IsPrincipalCongruenceSubgroup(H) then
return PrincipalCongruenceSubgroup( Lcm( LevelOfCongruenceSubgroup(G),
LevelOfCongruenceSubgroup(H) ) );
elif IsCongruenceSubgroupGamma1(G) and IsCongruenceSubgroupGamma1(H) then
return CongruenceSubgroupGamma1( Lcm( LevelOfCongruenceSubgroup(G),
LevelOfCongruenceSubgroup(H) ) );
elif IsCongruenceSubgroupGammaUpper1(G) and IsCongruenceSubgroupGammaUpper1(H) then
return CongruenceSubgroupGammaUpper1( Lcm( LevelOfCongruenceSubgroup(G),
LevelOfCongruenceSubgroup(H) ) );
elif IsCongruenceSubgroupGamma0(G) and IsCongruenceSubgroupGamma0(H) then
return CongruenceSubgroupGamma0( Lcm( LevelOfCongruenceSubgroup(G),
LevelOfCongruenceSubgroup(H) ) );
elif IsCongruenceSubgroupGammaUpper0(G) and IsCongruenceSubgroupGammaUpper0(H) then
return CongruenceSubgroupGammaUpper0( Lcm( LevelOfCongruenceSubgroup(G),
LevelOfCongruenceSubgroup(H) ) );
#
# Case 3 - Subgroups has different level
#
elif LevelOfCongruenceSubgroup(G) <> LevelOfCongruenceSubgroup(H) then
return IntersectionOfCongruenceSubgroups(G,H);
#
# Now subgroups have the same level
#
elif IsCongruenceSubgroupGamma1(G) and IsCongruenceSubgroupGamma0(H) then
return G; # so all properties and attributes of G will be preserved
elif IsCongruenceSubgroupGamma0(G) and IsCongruenceSubgroupGamma1(H) then
return H;
elif IsCongruenceSubgroupGammaUpper1(G) and IsCongruenceSubgroupGammaUpper0(H) then
return G;
elif IsCongruenceSubgroupGammaUpper0(G) and IsCongruenceSubgroupGammaUpper1(H) then
return H;
elif IsCongruenceSubgroupGamma0(G) and IsCongruenceSubgroupGammaUpper0(H) or IsCongruenceSubgroupGammaUpper0(G) and IsCongruenceSubgroupGamma0(H) then
return IntersectionOfCongruenceSubgroups(G,H);
else
return PrincipalCongruenceSubgroup(LevelOfCongruenceSubgroup(G));
fi;
end);
#############################################################################
##
## Indices of congruence subgroups
##
#############################################################################
InstallMethod( Index,
"for a natural SL_2(Z) and a congruence subgroup",
[ IsNaturalSL, IsCongruenceSubgroup ],
0,
function( G, H )
local n, prdiv, r, p;
n := LevelOfCongruenceSubgroup(H);
if HasIsPrincipalCongruenceSubgroup( H ) and
IsPrincipalCongruenceSubgroup( H ) then
if n=1 then
Assert( 1, IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) = 1 );
return 1;
elif n=2 then
Assert( 1, IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) = 6 );
return 12; # not 6, since we are in SL, not in PSL
else
prdiv := Set( Factors( n ) );
r := n^3; # not (n^3)/2 since we are in SL, not in PSL
for p in prdiv do
r := r*(1-1/p^2);
od;
Assert( 1, IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) = r/2 );
return r;
fi;
elif ( HasIsCongruenceSubgroupGamma0( H ) and IsCongruenceSubgroupGamma0( H ) ) or
( HasIsCongruenceSubgroupGammaUpper0( H ) and IsCongruenceSubgroupGammaUpper0( H ) ) then
# for CongruenceSubgroupGamma0 we use the formula
# [ SL_2(Z) : CongruenceSubgroupGamma0(n) ] = n * "Product over prime p | n" ( 1 + 1/p )
prdiv := Set( Factors( n ) );
r := n;
for p in prdiv do
r := r*(1+1/p);
od;
Assert( 1, IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) = r );
return r;
elif ( HasIsCongruenceSubgroupGamma1( H ) and IsCongruenceSubgroupGamma1( H ) ) or
( HasIsCongruenceSubgroupGammaUpper1( H ) and IsCongruenceSubgroupGammaUpper1( H ) ) then
# for CongruenceSubgroupGamma1 we use the formula
# [ CongruenceSubgroupGamma0(n) : CongruenceSubgroupGamma1(n) ] = n * "Product over prime p | n" ( 1 - 1/p )
# Combining with the previous case, we get that
# [ SL_2(Z) : CongruenceSubgroupGamma1(n) ] = n^2 * "Product over prime p | n" ( 1 - 1/p^2 )
prdiv := Set( Factors( n ) );
r := n^2;
for p in prdiv do
r := r*(1-1/p^2);
od;
Assert( 1, IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) = r/2 );
return r;
else
# if H is not in any of the cases above, for example is an intersection
# of some congruence subgroups, we derive the index from its Farey symbol
if [[-1,0],[0,-1]] in H then
return IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) ;
else
return IndexInPSL2ZByFareySymbol( FareySymbol ( H ) ) * 2;
fi;
fi;
end);
InstallMethod( IndexInSL2Z,
"for a congruence subgroup",
[ IsCongruenceSubgroup ],
0,
G -> Index( SL(2,Integers), G ) );
InstallMethod( Index,
"for a pair of congruence subgroups",
[ IsCongruenceSubgroup, IsCongruenceSubgroup ],
0,
function( G, H )
if IsSubgroup( G, H ) then
return IndexInSL2Z(H)/IndexInSL2Z(G);
fi;
end);
#############################################################################
##
## Generators of confruence subgroups from Farey symbols
##
#############################################################################
InstallMethod( GeneratorsOfGroup,
"for a congruence subgroup",
[ IsCongruenceSubgroup ],
0,
function(G)
local gens, i;
Info( InfoCongruence, 1, "Using the Congruence package for GeneratorsOfGroup ...");
gens := GeneratorsByFareySymbol( FareySymbol( G ) );
for i in [ 1 .. Length(gens) ] do
if not gens[i] in G then
gens[i] := -gens[i];
Assert( 1, gens[i] in G );
fi;
od;
return gens;
end );
#############################################################################
##
#E
##