/* Assorted code related to Galois groups of local fields */ /* Compute local galois groups, assuming pol irreducible over Q_p This function is for degrees < 8. fullentry is the standard format of the database, assuming all of the easier entries are filled in. In particular we need the following entries: 3: e 4: f 14: disc root field 19: twin polynomial for sextics 22: number of automorphisms */ localgalois(pol, p, fullentry) = { local(cnt,iseven, res, resf, dgs, f2, f3); /* Unramified, so cyclic (of degree of the field) */ if(fullentry[3]==1, return(dTt(fullentry[4], 1))); iseven= (fullentry[14] == [1,0]); /* use disc. root field */ /* Degrees 1 and 2 are easy enough */ if(poldegree(pol)==1, return(dTt(1,1))); if(poldegree(pol)==2, return(dTt(2,1))); /* Degree 3, just check if it is even */ if(poldegree(pol)==3, if(fullentry[22]>1, return(dTt(3,1)), return(dTt(3,2)))); /* Degree 4, use automorphisms and parity */ if(poldegree(pol)==4, cnt=fullentry[22]; if(cnt==2, return(dTt(4,3))); if(cnt==1, if(iseven==0, return(dTt(4,5)), return(dTt(4,4))), if(iseven==0, return(dTt(4,1)), return(dTt(4,2))))); /* Degree 5, automorphisms and parity suffice */ if(poldegree(pol)==5, if(fullentry[22]>1, return(dTt(5,1))); if(iseven==0, return(dTt(5,3)), return(dTt(5,2)))); /* Degree 6, assumes a twin poly is in entry 19 */ if(poldegree(pol)==6, res=fullentry[19]; resf=factorpadic(res,p,50); /* Degrees of the factors of the twin poly, which we then sort */ dgs=vector(length(resf[,1]~), h, poldegree(resf[h,1])); dgs=vecsort(dgs); if(length(dgs) == 1, /* Twin is irreducible */ if(iseven==1, return(dTt(6,10)), return(dTt(6,13)))); if(dgs==[1,2,3], /* Check if the cubic factor is A_3 or S_3 */ f2=resf[1,1]; if(poldegree(f2)<3, f2=resf[2,1]; if(poldegree(f2)<3,f2=resf[3,1])); f2=lift(f2*Mod(1,p^50)); if(getdisc(f2,p)==[1,0], return(dTt(6,1)), return(dTt(6,3)))); /* Two cubic factors, check both */ if(dgs==[3,3], f2=lift(resf[1,1]*Mod(1,p^50)); f3=lift(resf[2,1]*Mod(1,p^50)); if((getdisc(f2,p)!=[1,0]) && (getdisc(f3,p)!=[1,0]), return(dTt(6,9)), return(dTt(6,5)))); if(dgs==[2,4], if(iseven, return(dTt(6,7))); f2=resf[1,1]; if(poldegree(f2)==2, f2=resf[2,1]); f2=lift(f2*Mod(1,p^50)); if(getdisc(f2,p)==[1,0], return(dTt(6,6)), return(dTt(6,11)))); if(dgs==[1,1,4], if(iseven, return(dTt(6,4)), return(dTt(6,8)))); if(dgs==[1,1,1,3], return(dTt(6,2))); return([0,0,0])); /* This works for septics, but we don't use it since there are easier ways for septics over local fields */ if(poldegree(pol)==7, if(fullentry[22]>1, return(dTt(7,1))); if(iseven==1, return(dTt(7,3))); res=myseptres(pol); resf=factorpadic(res,p,50); if(length(resf[,1]~)==4, return(dTt(7,2)), return(dTt(7,4)))); return([0,0,0]) } /* New Galois group scheme [|G|, type, [data], print] print says how to print this group, some types may have a default The data depends on the type. The known types are: "t" for T notation of the current degree "i" for an intransitive group with background data "a" for an abstract transitive group, no t-number available "o" for just the order of the group "tu" for C_t.C_u partial info "e" for the trivial group */ /* Generate new codes for transitive subgroups of S_n. The point here is to case out ones where we have a preferred name, otherwise a generic fallback is used. gord is rarely supplied, but it is the order of the group */ dTt(n, t, gord=0) = { if(n==1, return([n, "e", [], "<e>"])); if(t==1, return([n, "t", [n, 1], Str("C" n "")])); if(n==3 && t==2, return([6, "t", [n,t], "S3"])); if(n==4 && t==2, return([4, "t", [n,t], "V4"])); if(n==4 && t==3, return([8, "t", [n,t], "D4"])); if(n==4 && t==4, return([12, "t", [n,t], "A4"])); if(n==4 && t==5, return([24, "t", [n,t], "S4"])); if(n==5 && t==2, return([10, "t", [n,t], "D5"])); if(n==5 && t==3, return([20, "t", [n,t], "F5"])); if(n==6 && t==2, return([6, "t", [n,t], "S3"])); if(n==6 && t==3, return([12, "t", [n,t], "D6"])); if(n==6 && t==4, return([12, "t", [n,t], "A4"])); if(n==6 && t==5, return([18, "t", [n,t], "S3C3"])); if(n==6 && t==6, return([24, "t", [n,t], "A4C2"])); if(n==6 && t==7, return([24, "t", [n,t], "S4+"])); if(n==6 && t==8, return([24, "t", [n,t], "S4"])); if(n==6 && t==9, return([36, "t", [n,t], "S32"])); if(n==6 && t==10, return([36, "t", [n,t], "C32 : C4"])); if(n==6 && t==11, return([48, "t", [n,t], "S4C2"])); if(n==6 && t==13, return([72, "t", [n,t], "C32 : D4"])); if(n==7 && t==2, return([14, "t", [n,t], "D7"])); if(n==7 && t==3, return([14, "t", [n,t], "C7:C3"])); if(n==7 && t==4, return([14, "t", [n,t], "F7"])); if(n==8 && t==2, return([8, "t", [n,t], "C4C2"])); if(n==8 && t==3, return([8, "t", [n,t], "C23"])); if(n==8 && t==4, return([8, "t", [n,t], "D4"])); if(n==8 && t==5, return([8, "t", [n,t], "Q8"])); if(n==8 && t==6, return([16, "t", [n,t], "D8"])); if(n==8 && t==9, return([16, "t", [n,t], "D4C2"])); if(n==8 && t==12, return([24, "t", [n,t], "SL2(3)"])); if(n==8 && t==13, return([24, "t", [n,t], "A4C2"])); if(n==8 && t==14, return([24, "t", [n,t], "S4"])); if(n==8 && t==23, return([48, "t", [n,t], "GL2(3)"])); if(n==8 && t==24, return([48, "t", [n,t], "S4C2"])); if(n==8 && t==25, return([56, "t", [n,t], "C23:C7"])); if(n==8 && t==36, return([168, "t", [n,t], "C23:(C7:C3)"])); if(n==9 && t==2, return([8, "t", [n,t], "C32"])); if(n==9 && t==3, return([18, "t", [n,t], "D9"])); if(n==9 && t==4, return([18, "t", [n,t], "S3C3"])); if(n==9 && t==5, return([18, "t", [n,t], "C32 : C2"])); if(n==9 && t==8, return([36, "t", [n,t], "S32"])); if(n==9 && t==9, return([36, "t", [n,t], "C32 : C4"])); if(n==9 && t==16, return([72, "t", [n,t], "C32 : D4"])); if(n==10 && t==2, return([10, "t", [n,t], "D5"])); if(n==10 && t==3, return([20, "t", [n,t], "D10"])); if(n==10 && t==4, return([20, "t", [n,t], "F5"])); if(n==10 && t==5, return([40, "t", [n,t], "F5C2"])); if(n==10 && t==6, return([50, "t", [n,t], "C5wrC2"])); if(n==11 && t==2, return([22, "t", [n,t], "D11"])); if(n==11 && t==3, return([55, "t", [n,t], "C11:C5"])); if(n==11 && t==4, return([110, "t", [n,t], "F11"])); if(n==12 && t==2, return([12, "t", [n,t], "C6C2"])); if(n==12 && t==3, return([12, "t", [n,t], "D6"])); if(n==12 && t==4, return([12, "t", [n,t], "A4"])); if(n==12 && t==5, return([12, "t", [n,t], "C3 : C4"])); if(n==12 && t==11, return([24, "t", [n,t], "S3C4"])); if(n==12 && t==14, return([24, "t", [n,t], "D4C3"])); if(n==13 && t==2, return([26, "t", [n,t], "D13"])); if(n==13 && t==3, return([39, "t", [n,t], "C13:C3"])); if(n==13 && t==4, return([52, "t", [n,t], "C13:C4"])); if(n==13 && t==5, return([78, "t", [n,t], "C13:C6"])); if(n==13 && t==6, return([156, "t", [n,t], "F13"])); return([gord, "t", [n,t]]); /* Order has to be filled in elsewhere */ } /* Companion for cases known to be coded by T-number */ gett(g) = g[3][2] /* For dynamic field generation, quick Galois group computation. Either this is C_p : C_d for some d, or it is a tame extension. Inputs: lis is a pair [a, b], and the group is definitely C_a.C_b, (but maybe we can do better). In tame cases, [a,b] is [t,u]. ent is a full entry for the field. In particular, we may need its subfields. If the group is not transitive (some inertia groups), intrans is set to 1. */ qgal(lis, ent, intrans=0) = { local(smord,g,myn, subgal); myn = ent[5]; /* gets the degree of the original extension */ if(lis[2]==1, /* Group is cyclic */ if(lis[1]==1, return(dTt(1,1))); /* Trivial group */ g=dTt(lis[1],1); if(intrans, g[2]="i"); return(g)); /* Now not obviously cyclic */ if(myn==3, if(lis[1]*lis[2]==3, return(dTt(3,1))); return(dTt(3,2))); if(myn==5, if(lis[1]*lis[2] == 5, return(dTt(5,1))); if(lis[1]*lis[2] == 10, return(dTt(5,2))); if(lis[1]*lis[2] == 20, return(dTt(5,3)))); if(myn==6 && ent[20] != 0, if(lis[1]*lis[2] == 6, if(length(ent[20]) == 2, return(dTt(6,1)), return(dTt(6,2)))); if(lis[1]*lis[2] == 12, return(dTt(6,3))); if(lis[1]*lis[2] ==18, return(dTt(6,5)))); if(myn==7, if(lis[1]*lis[2] == 7, return(dTt(7,1))); if(lis[1]*lis[2] == 14, return(dTt(7,2))); if(lis[1]*lis[2] == 21, return(dTt(7,3))); if(lis[1]*lis[2] == 42, return(dTt(7,4)))); if(myn==8, if(ent[18] != 0, if(lis[1]*lis[2]==8, if(length(ent[18][1][2])==1, return(dTt(8,1))); if(length(ent[18][2][2])==1, return(dTt(8,5))); smord=0; for(j=1,length(ent[18][2][2]), smord=max(smord, subdegs[4][indexofpoly(ent[18][2][2][j], subdegs[4])][8])); if(smord==4, return(dTt(8,2))); if(smord==8, return(dTt(8,4))); , /* group of order 16 */ smord=0; for(j=1,length(ent[18][2][2]), smord=max(smord, subdegs[4][indexofpoly(ent[18][2][2][j], subdegs[4])][8])); if(smord==4, return(dTt(8,7))); /* Uggh, we need to use a8res -> irred means T8, else T6 */ smord = a8degs(ent[6], ent[1]); if(smord==[4,4], return(dTt(8,6))); if(smord==[8], return(dTt(8,8))); ))); if(myn==9, if(lis[1]*lis[2] == 9, if(ent[18] != 0, if(length(ent[18][1][2])==1, return(dTt(9,1))); if(length(ent[18][1][2])==4, return(dTt(9,2))))); /* if(lis[1]*lis[2] == 11, return(Str("C11"))); */ if(lis[1]*lis[2] == 18, if(lis[1]==9, return(dTt(9,3)), return(dTt(9,4)))); if(lis[1]*lis[2] == 27, return(dTt(9,6))); if(lis[1]*lis[2] == 54, return(dTt(9,10)))); if(myn==10, if(lis[1]*lis[2] == 50, return(dTt(10,6))); if(lis[1]*lis[2] == 40, return(dTt(10,5))); if(lis[1]*lis[2] == 20, if(lis[1]==10, return(dTt(10,3))); if(lis[1]==5, return(dTt(10,4))); ); if(lis[1]*lis[2] == 10, /* look at galois group of quintic sub */ if(ent[18] != 0, smord=0; for(j=1,length(ent[18][2][2]), smord=max(smord, subdegs[2][indexofpoly(ent[18][2][2][j], subdegs[2])][8])); if(smord==10, return(dTt(10,2))); if(smord==5, return(dTt(10,1))); ); ); ); /* end of degree 10 */ if(myn==11, if(lis[1]*lis[2] == 11, return(dTt(11,1))); if(lis[1]*lis[2] == 22, return(dTt(11,2))); if(lis[1]*lis[2] == 55, return(dTt(11,3))); if(lis[1]*lis[2] == 110, return(dTt(11,4)))); /* end of degree 11 */ if(myn==12, if(length(ent[18][1][2])==3, /* 3 quadratic subs */ if(length(ent[18][4][2])==1, return(dTt(12,18))); smord = subdegs[3][indexofpoly(ent[18][2][2][1], subdegs[3])][7][3][2]; /* degree 3 galois T number */ if(smord==1, return(dTt(12,2))); if(smord==2, return(dTt(12,3))); /* should not get here */ return(1/0); ); /* 1 quadratic sub */ smord = subdegs[6][indexofpoly(ent[18][4][2][1], subdegs[6])][7][3][2]; /* degree 6 galois T number */ if(smord == 2, return(dTt(12,5))); if(smord == 5, return(dTt(12,19))); subgal = subdegs[4][indexofpoly(ent[18][3][2][1], subdegs[4])][7][3][2]; /* degree 4 galois T number */ if(smord == 1, if(subgal == 1, return(dTt(12,1))); if(subgal == 3, return(dTt(12,14)))); if(smord == 3, if(subgal == 1, return(dTt(12,11))); if(subgal == 3, return(dTt(12,12))))); /* end of degree 12 */ if(myn==13, if(lis[1]*lis[2] == 13, return(dTt(13,1))); if(lis[1]*lis[2] == 13*2, return(dTt(13,2))); if(lis[1]*lis[2] == 13*3, return(dTt(13,3))); if(lis[1]*lis[2] == 13*4, return(dTt(13,4))); if(lis[1]*lis[2] == 13*6, return(dTt(13,5))); if(lis[1]*lis[2] == 13*12, return(dTt(13,6)))); /* end of degree 13 */ /* Default fallback */ return([lis[1]*lis[2], "tu", [lis[1], lis[2]], Str("C"lis[1]".C"lis[2]"")]); } /* A few resolvents, first sextic twins */ mytwin(pol) = { local(tmp, ttmp); tmp=pol; while(issquarefree(ttmp=round(twin(tmp)))==0, tmp=poltschirnhaus(pol)); ttmp } from6rts(r) = r[1]^2*r[5]^2*(r[2]*r[4] + r[3]*r[6]) + \ r[2]^2*r[4]^2*(r[1]*r[5] + r[3]*r[6]) + \ r[3]^2*r[6]^2*(r[1]*r[5] + r[2]*r[4]) + \ r[1]^2*r[6]^2*(r[2]*r[5] + r[3]*r[4]) + \ r[2]^2*r[5]^2*(r[1]*r[6] + r[3]*r[4]) + \ r[3]^2*r[4]^2*(r[1]*r[6] + r[2]*r[5]) + \ r[1]^2*r[3]^2*(r[2]*r[6] + r[4]*r[5]) + \ r[2]^2*r[6]^2*(r[1]*r[3] + r[4]*r[5]) + \ r[4]^2*r[5]^2*(r[1]*r[3] + r[2]*r[6]) + \ r[1]^2*r[4]^2*(r[2]*r[3] + r[5]*r[6]) + \ r[2]^2*r[3]^2*(r[1]*r[4] + r[5]*r[6]) + \ r[5]^2*r[6]^2*(r[1]*r[4] + r[2]*r[3]) + \ r[1]^2*r[2]^2*(r[3]*r[5] + r[4]*r[6]) + \ r[3]^2*r[5]^2*(r[1]*r[2] + r[4]*r[6]) + \ r[4]^2*r[6]^2*(r[1]*r[2] + r[3]*r[5]) twin(pol) = { local(rts, rts2, rtsc); rts = polroots(pol); rtsc = rts; rts2 = vector(6,h,0); rts2[1] = from6rts(rts); for(j=2,6, rts=rtsc; tmp=rts[1]; rts[1]=rts[j]; rts[j]=tmp; rts2[j] = from6rts(rts)); prod(j=1,6,x-rts2[j]) } /* We don't use it, but here is the degree 35 resolvent for septics */ septres(pol) = { local(rts); rts = polroots(pol); prod(j=1,5,prod(k=j+1,6,prod(l=k+1,7, x-(rts[j]+rts[k]+rts[l])))) } myseptres(pol) = { local(tmp, ttmp); tmp=pol; while(issquarefree(ttmp=round(septres(tmp)))==0,tmp=poltschirnhaus(pol)); real(ttmp) } a8(evenpol) = { local(a,b,c,d); a = polcoeff(evenpol,6); b = polcoeff(evenpol,4); c = polcoeff(evenpol,2); d = polcoeff(evenpol,0); a^8 - 16*a^6*b + 96*a^4*b^2 - 256*a^2*b^3 + 256*b^4 - 128*a^4*d + 1024*a^2*b*d - 2048*b^2*d + 4096*d^2 + 8*a^7*x - 96*a^5*b*x + 384*a^3*b^2*x - 512*a*b^3*x + 128*a^4*c*x - 1024*a^2*b*c*x + 2048*b^2*c*x + 1536*a^3*d*x - 6144*a*b*d*x + 8192*c*d*x + 28*a^6*x^2 - 240*a^4*b*x^2 + 576*a^2*b^2*x^2 - 256*b^3*x^2 + 512*a^3*c*x^2 - 2048*a*b*c*x^2 + 4096*c^2*x^2 + 1280*a^2*d*x^2 - 7168*b*d*x^2 + 56*a^5*x^3 - 320*a^3*b*x^3 + 384*a*b^2*x^3 + 768*a^2*c*x^3 - 1024*b*c*x^3 - 2560*a*d*x^3 + 70*a^4*x^4 - 240*a^2*b*x^4 + 96*b^2*x^4 + 512*a*c*x^4 - 2176*d*x^4 + 56*a^3*x^5 - 96*a*b*x^5 + 128*c*x^5 + 28*a^2*x^6 - 16*b*x^6 + 8*a*x^7 + x^8 } isevenpol(poly) = { for(j=1,poldegree(poly)-1, if((j%2 !=0) && polcoeff(poly,j) != 0, return(0))); return(1) } /* Does a8 resolvant and returns the type */ a8degs(pol, p) = { local(a8pol, fp, degs); if(isevenpol(pol) == 0, print("Sorry, internal error in a8type.");return([1,2,3,4,5])); a8pol = a8(pol); fp=factorpadic(a8pol,p,100); degs = vector(length(fp[,1]), h, poldegree(fp[h,1])); if(sum(j=1,length(degs), degs[j]) != 8, print("Sorry, non-separable resolvent."); return([1,2,3,4,5])); return(vecsort(degs)); }