GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W factor.gi The Congruence package Helena Verrill
##
##
#############################################################################
# it will be useful to find the maximum value of the labels
# though if space is not a problem, this could just return
# the Length of the labels.
__congruence_max_label:= function(L)
local s, i;
s:=1;
for i in [1..Length(L)] do
if (not L[i] = "even") and (not L[i] = "odd") and L[i] > s then
s := L[i];
fi;
od;
return s;
end;;
# For a list of labels L such as
# [1,3,4,7,4,7,3,1,"odd","even"], for reference, indices are:
# 1 2 3 4 5 6 7 8 9 10
# want to produce a list:
# [[9],[10],[1,8],[],[2,7],[3,5],...]
# this is the list of the form:
# [[all indices with L[x] = "odd"],[all indices with L[x] = "even"],
# [all indices with L[x] = 1], ....]
# assume L is a list of integers, or "odd" or "even".
__congruence_edgepairs := function(L)
local max, pairs, i;
pairs:=[];
max:=__congruence_max_label(L);
for i in [1..max+2] do
pairs[i] := [];
od;
for i in [1..Length(L)] do
if L[i]="odd" then
Add(pairs[1],i);
elif L[i]="even" then
Add(pairs[2],i);
else
Add(pairs[L[i]+2],i);
fi;
od;
return pairs;
end;;
# for each edge of a Farey Symbol, we compute the generator
# which maps that edge to another edge.
# (this is done at the same time as the fundamental
# domain is computed, but the data may not have been stored,
# and has to be recomputed; suggest change for a future version)
# this function gives "edge gluing matrices" as a number in the
# list of generators (gens); negative entries mean the inverse matrix,
# e.g., -5 would mean (5th generator)^(-1)
# (note, the list of labels in a Farey sequence says which edge is
# glued to which; -2 and -3 means there is an elliptic point order
# 2 or 3)
#
# the input is assumed to be a FareySymbol;
# another version of this function could take input to be the group
#
# Note, if the output of this function was
# stored as an attribute of the FareySymbol,
# then it would not have to be recomputed
#
__congruence_gluing_matrices := function(FS)
local cusps, gens, label_list, glue_list, l, i, index, gfs, labels, matrix;
# the following is a list of the cusps of the sequence,
# and other data extracted from the FareySymbol
gfs := GeneralizedFareySequence(FS);
labels := LabelsOfFareySymbol(FS);
gens := GeneratorsByFareySymbol( FS );
# make a list of which edges have a given label:
label_list := __congruence_edgepairs(labels);
# the following list will be what is finally returned,
# a list of integers as described above.
glue_list := [];
# make list of which generator joins two edges,
# in the non elliptic case
for i in [3..Length(label_list)] do
l := label_list[i];
matrix := MatrixByFreePairOfIntervals( gfs, l[1], l[2] );
index := PositionNthOccurrence( gens ,matrix,1);
if index = "fail" then
index := -PositionNthOccurrence(gens,matrix^(-1),1);
fi;
glue_list[l[1]] := index;
glue_list[l[2]] := -index;
od;
# Now deal with elliptic elements:
for i in label_list[1] do
matrix := MatrixByOddInterval( gfs, i );
index := PositionNthOccurrence(gens,matrix,1);
if index = "fail" then
index := -PositionNthOccurrence(gens,matrix^(-1),1);
glue_list[i] := -index;
else
glue_list[i] := -index;
fi;
od;
for i in label_list[2] do
matrix := MatrixByEvenInterval( gfs, i );
index := PositionNthOccurrence(gens,matrix,1);
if index = "fail" then
index := -PositionNthOccurrence(gens,matrix^(-1),1);
glue_list[i] := -index;
else
glue_list[i] := -index;
fi;
od;
return glue_list;
end;;
# following function determines which edge an image ImL of
# a domain is the longest
#
# The function either returns a index of an edge
# which is a number between 1 and #L-1,
# or it returns "overlap" meaning that there is overlap, but not equality.
__congruence_longest_edge := function(ImL)
local i, minImL, maxImL, maxindex, minindex;
for i in [1..Length(ImL)] do
if ImL[i] = infinity then
return "infinity";
fi;
od;
minImL := Minimum(ImL);
maxImL := Maximum(ImL);
maxindex := PositionNthOccurrence( ImL ,maxImL,1);
return maxindex;
end;;
# Need to be able to apply action of matrices to cusps
__congruence_fractionallineartransformation:= function(g,c)
local den, num;
if c = infinity then
if g[2][1] = 0 then
return infinity;
else
return g[1][1]/g[2][1];
fi;
else
num:=g[1][1]*c + g[1][2];
den:=g[2][1]*c + g[2][2];
if den = 0 then
return infinity;
else
return num/den;
fi;
fi;
end;;
__congruence_PSL2multiply := function(g,L)
local imL, i;
imL := [];
for i in [1..Length(L)] do
Add(imL,__congruence_fractionallineartransformation(g,L[i]));
od;
return imL;
end;;
# this an algorithm to determine a word for
# a given matrix g in G in terms of the generators:
find_word_ver2 := function(FS,glue_list,g)
local gens, L, ImL, done, word,letter,i, edge, h, maybesame, inf;
gens := GeneratorsByFareySymbol( FS );
L := GeneralizedFareySequence( FS );
ImL := __congruence_PSL2multiply(g,L);
word:=[];
h := g;
done := false;
while not done do;
edge := __congruence_longest_edge(ImL);
if edge = "infinity" then
# check equality of L and ImL:
maybesame := true;
i := 1;
while i < Length(L) and maybesame do
if not L[i] = ImL[i] then
maybesame := false;
fi;
i := i+1;
od;
if maybesame then
done := true;
return Reversed(word);
fi;
# now assume the domains are not equal
inf := PositionNthOccurrence( ImL , infinity ,1);
if inf = 1 and
ImL[2]<L[Length(L)-1] and
ImL[Length(L)-1]>L[2] then
return "g is not in the group";
elif
ImL[i+1]<L[Length(L)-1] and
ImL[i-1]>L[2] then
return "g is not in the group";
fi;
# now assume the domains do not overlap
if ImL[inf+1] >= L[2] then
letter := glue_list[inf];
elif inf = 1 then
letter := glue_list[Length(glue_list)];
else
letter := glue_list[inf-1];
fi;
Add(word,letter);
h:=h*gens[AbsoluteValue(letter)]^(-SignInt(letter));
ImL := __congruence_PSL2multiply(h,L);
else
# get next "letter" in the word for the matrix:
letter := glue_list[edge];
Add(word,letter);
h:=h*gens[AbsoluteValue(letter)]^(-SignInt(letter));
ImL := __congruence_PSL2multiply(h,L);
fi;
od;
return Reversed(word);
end;;
#############################################################################
#
# FactorizeMat( G, g )
#
__congruence_FactorizeMat := function( G, g )
return find_word_ver2( FareySymbol(G),
__congruence_gluing_matrices(FareySymbol(G)),
g );
end;
#############################################################################
#
# CheckFactorizeMat(gens,word)
#
# the following function is for testing purposes:
# gens is a list of generators, "word" a sequence of integers, none
# of which is bigger than the size of the list of generators.
# a word [4,6,-3] will return the product gens[4]*gens[6]*gens[3]^(-1)
#
__congruence_CheckFactorizeMat := function(gens,word)
local g, i;
g := [[1,0],[0,1]];
for i in word do
g := g*gens[AbsoluteValue(i)]^SignInt(i);
od;
return g;
end;