# For any cromulent surface, we can consider the full subgroupoid of # the fundamental groupoid whose objects are v[0],...,v[13]. # This file sets up definitions for this groupoid. It does not # do everything that one might like. Ideally, we should reconcile # the whole structure of Pi_tilde with the groupoid picture. # The edges of F16 give four elements of the groupoid, which # we label p,q,r and s. These have different stabilisers, # but in each case the group G8 =is a comlement for # the stabiliser, so the G8 orbits of p,q,r,s and their # inverses give a system of generators that is invariant under # inversion and under the action of G. # A triple [T,g,i] represents the image under T of g^i, where # T is an element of G and g is in {p,q,r,s} and i is in {1,-1}. #@ Gamma_gens Gamma_gens := [seq(seq(seq([T,g,i],i in [1,-1]),T in G8),g in ["p","q","r","s"])]: # Stabilisers #@ pg_stab pg_stab := table(["p" = N, "q" = LLN, "r" = LN, "s" = MN]): # p lies along C[5], q along C[3], r along C[1] and s along C[0] #@ pg_curve pg_curve := table(["p" = 5, "q" = 3, "r" = 1, "s" = 0]): # Source vertices #@ pg_src0 pg_src0 := table(["p" = 0, "q" = 11, "r" = 0, "s" = 6]): # Target vertices #@ pg_tgt0 pg_tgt0 := table(["p" = 11, "q" = 3, "r" = 6, "s" = 3]): # Action of G on the set of generators #@ pg_act pg_act := proc(U,Tgi) local T,g,i,UT; T,g,i := op(Tgi); UT := G_mult(U,T); if not member(UT,G8) then UT := G_mult(UT,pg_stab[g]); fi; return [UT,g,i]; end: # Inversion of generators #@ pg_inv pg_inv := (Tgi) -> [Tgi[1],Tgi[2],-Tgi[3]]: # This function sends a generator to its source vertex #@ pg_src pg_src := proc(Tgi) local T,g,i,j; T,g,i := op(Tgi); j := `if`(i = 1,pg_src0[g],pg_tgt0[g]); return act_V[T](j); end: # This function sends a generator to its target vertex #@ pg_src pg_tgt := (Tgi) -> pg_src(pg_inv(Tgi)): #@ `type/Gamma_element` `type/Gamma_element` := proc(PP) local n,m,i,j; if not(type(PP,list)) then return false; fi; n := nops(PP); if not type(n,odd) then return false; fi; m := (n-1)/2; for i from 0 to m do j := PP[2*i+1]; if not(type(j,integer) and j >= 0 and j < 14) then return false; fi; od; for i from 1 to m do if pg_tgt(PP[2*i]) <> PP[2*i-1] then return false; fi; if pg_src(PP[2*i]) <> PP[2*i+1] then return false; fi; od: return true; end: Gamma_src := (PP) -> PP[nops(PP)]: #@ Gamma_src Gamma_tgt := (PP) -> PP[1]: #@ Gamma_tgt #@ Gamma_reduce Gamma_reduce := proc(PP) local n,m,i,P,QQ,changed; n := nops(PP); if not type(n,odd) then return FAIL; fi; m := (n-1)/2; QQ := [PP[1]]; changed := false; for i from 1 to m do if nops(QQ) > 1 and PP[2*i] = pg_inv(QQ[nops(QQ)-1]) then QQ := [op(1..-3,QQ)]; changed := true; else QQ := [op(QQ),PP[2*i],PP[2*i+1]]; fi; od: if changed then return Gamma_reduce(QQ); else return QQ; fi; end: # This implements composition of paths. The convention is that # Gamma_o(P,Q) is Q followed by P, so the target of Q should be # the source of P. Any strictly positive number of arguments # can be given. #@ Gamma_o Gamma_o := proc() local PP,QQ,RR; if nargs = 0 then return FAIL; elif nargs = 1 then return args[1]; else PP := args[1]; QQ := args[2]; if Gamma_src(PP) <> Gamma_tgt(QQ) then return FAIL; fi; RR := Gamma_reduce([op(1..-2,PP),op(QQ)]); if nargs = 2 then return RR; else return Gamma_o(RR,args[3..-1]); fi; fi; end: # This implements inversion of paths #@ Gamma_inv Gamma_inv := proc(PP) local n,m,i,QQ; n := nops(PP); if not type(n,odd) then return FAIL; fi; m := (n-1)/2; QQ := PP[n]; for i from 1 to m do QQ := QQ,pg_inv(PP[2*(m+1-i)]),PP[2*(m-i)+1]; od: return [QQ]; end: # Gamma_id(i) is the identity path at vertex i #@ Gamma_id Gamma_id := (i) -> [i]: # Our generators are represented in a slightly different form from # general elements of Gamma. This function performs the required # conversion. #@ gen_path gen_path := (Tgi) -> [pg_tgt(Tgi),Tgi,pg_src(Tgi)]: # This implements the action of G on Gamma #@ Gamma_act Gamma_act := proc(T,PP) local n,m,i,P,QQ; n := nops(PP); if not type(n,odd) then return FAIL; fi; m := (n-1)/2; QQ := act_V[T](PP[1]); for i from 1 to m do QQ := QQ,pg_act(T,PP[2*i]),act_V[T](PP[2*i+1]); od: return [QQ]; end: # Each of our generators is equivalent to a word of length three in # some other generators, representing a path that goes around the # opposite side of an appropriate fundamental domain. This table # records the details. #@ Gamma_flip0 Gamma_flip0 := table([ "p" = [11,[1,"q",-1], 3,[1,"s", 1], 6,[1,"r", 1], 0], "q" = [ 3,[1,"s", 1], 6,[1,"r", 1], 0,[1,"p",-1],11], "r" = [ 6,[1,"s",-1], 3,[1,"q", 1],11,[1,"p", 1], 0], "s" = [ 3,[1,"q", 1],11,[1,"p", 1], 0,[1,"r",-1], 6] ]): # This function flips the k'th letter in the word PP, and then # simplifies the result. Each word lies on the boundary between # two different translates of F16, so there are two ways to flip # it; this can be controlled by supplying 0 or 1 as the third # argument l_. For convenience we also allow the argument k to # be zero, in which case the original word is returned unchanged. #@ Gamma_flip Gamma_flip := proc(PP,k,l_) local T,g,i,Q,l; if k = 0 then return PP; fi; l := `if`(nargs > 2,l_,0); T,g,i := op(PP[2*k]); if l > 0 then T := G_mult(T,pg_stab[g]); fi; Q := Gamma_act(T,Gamma_flip0[g]); if i = -1 then Q := Gamma_inv(Q); fi; Gamma_reduce([op(1..2*k-2,PP),op(Q),op(2*k+2..-1,PP)]); end: #@ Gamma_flip_multiple Gamma_flip_multiple := proc(PP,FF) local QQ,kl; QQ := PP; for kl in FF do QQ := Gamma_flip(QQ,op(kl)); od; return QQ; end: # The following function attempts to find a sequence FF of # flips that shortens PP. It will try FF of length at most r. # It returns [FF,QQ], where QQ is the result of flipping. #@ find_flip find_flip := proc(PP,r := 2) local m,i1,i2,j1,j2,QQ; if r = 0 then return [[],PP]; fi; m := (nops(PP)-1)/2; for i1 from 1 to m do for j1 from 0 to 1 do QQ := find_flip(Gamma_flip(PP,i1,j1),r-1); if nops(QQ[2]) < nops(PP) then return [[[i1,j1],op(QQ[1])],QQ[2]]; fi; od; od; return [[],PP]; end: # The following function applies find_flip() repeatedly to # shorten PP as much as possible. It again returns [FF,QQ], # where FF is a flipping sequence, and QQ is the result of # applying it. #@ find_flips find_flips := proc(PP,r := 2) local QQ,RR,EE,FF,ok; QQ := PP; FF := []; ok := true; while ok do EE,RR := op(find_flip(QQ,r)); if nops(RR) < nops(QQ) then QQ := RR; FF := [op(FF),op(EE)]; else ok := false; fi; od; return ([FF,QQ]); end: Gamma_u := [12, [LLLM, "p", 1], 1, [LLLM, "r", -1], 8, [LL, "r", 1], 0]; # These elements of Gamma have source = target = 0, so they correspond # to elements of the fundamental group Pi. In fact, Gamma_beta[i] # corresponds to beta[i]. #@ Gamma_beta Gamma_beta[0] := [0,[LL,"p",-1],11,[1,"p",1],0]; Gamma_beta[1] := Gamma_o(Gamma_inv(Gamma_act(LL,Gamma_u)),Gamma_u); Gamma_beta[2] := Gamma_act(L,Gamma_beta[0]); Gamma_beta[3] := Gamma_act(L,Gamma_beta[1]); Gamma_beta[4] := Gamma_act(L,Gamma_beta[2]); Gamma_beta[5] := Gamma_act(L,Gamma_beta[3]); Gamma_beta[6] := Gamma_act(L,Gamma_beta[4]); Gamma_beta[7] := Gamma_act(L,Gamma_beta[5]); # Gamma_base[i] is a path from v[i] to the basepoint v[0] #@ Gamma_base Gamma_base[ 0] := [ 0]; Gamma_base[ 1] := [ 1,[LM, "r",-1], 6,[1, "r", 1],0]; Gamma_base[ 2] := [ 2,[LLL,"q", 1],10,[LLL,"p", 1],0]; Gamma_base[ 3] := [ 3,[1, "q", 1],11,[1, "p", 1],0]; Gamma_base[ 4] := [ 4,[L, "q", 1],10,[L, "p", 1],0]; Gamma_base[ 5] := [ 5,[LL, "q", 1],11,[LL, "p", 1],0]; Gamma_base[ 6] := [ 6,[1, "r", 1], 0]; Gamma_base[ 7] := [ 7,[L, "r", 1], 0]; Gamma_base[ 8] := [ 8,[LL, "r", 1], 0]; Gamma_base[ 9] := [ 9,[LLL,"r", 1], 0]; Gamma_base[10] := [10,[L ,"p", 1], 0]; Gamma_base[11] := [11,[1 ,"p", 1], 0]; Gamma_base[12] := [12,[LM, "p", 1], 1,[LM, "r",-1], 6,[1,"r",1], 0]; Gamma_base[13] := [13,[LLM,"p", 1], 1,[LLM,"r",-1], 7,[L,"r",1], 0];