/* If you just want polynomials, you can comment these out and use
quickdegp function below */
read("panayi.gp");
read("localgalois.gp");
/* Routines here are for systematically generated p-adic extensions,
The main routine is doitall(p,n) which produces all of the data we
generate for the database in our "standard" format. It can be used
when p=n or when gcd(p,n)=1. Along the way, it generates the same
data for proper non-trivial divisors of n to help with subfields.
If you just want polynomials, you might use one of the following
routines. They can also be useful if you want say tame extensions
of Q_2 of degree 12 withe e=3. These do not produce full format.
What they do produce is documented below. See the definition of
"doitall" near the end of the file if you want to beef their output
up to standard format.
unrampoljr(n,p) -> the unramified extension, just the polynomial
tames(p,f,e) -> the tamely ramified extensions
padicdegp(p) -> the ramified degree p extensions of Qp
quickdegp(p,n) -> puts together a couple of these routines to throw
in the unramified extension with the others.
Format of the output of most of these routines is a list of
the form [poly, c, e, f, t, slopes, d-galois, d-inertia, unram, ram]
Here, t is tame degree of the Galois closure, slopes is a list of wild
slopes. The Galois group and inertia groups just give degrees (p-part
and tame part). Finally, it is helpful in other computations, such
as Panayi's algorithm, to represent the extension as a totally ramified
extension of an unramified extension. The unramified part part is in
the variable t and follows our standard choice of polynomial. The
ramified part is an Eisenstein polynomial in x or y with coefs which
are polynomials in t.
To convert this information to the same format used for wild extensions
of composite degree, it takes a couple of steps. First, use conv1
to do some basic conversions (syntax explained by the definition of conv1.
To support this, code is included for discriminant root fields, and
local root numbers.
At the end are rountines for
conway(n,p) -> compute the Conway polynomial
Routines provided here are provided without warrenty or much
documentation. If you want an easy interface to most of them,
use the web front-end to the database.
*/
/* A favorite utility, we wish gp had an built in version of this
The first input is a string giving the function to apply to each
element of the list "lis". The string gives the function in terms
of z, such as mapz("z^2", [1,2,3,4,5])
Note, you cannot nest instances of this function because the "special"
function forbarbaz1 will have a name clash from each instance.
*/
mapz(funcstring,lis)=(forbarbaz1(z)=eval(Str(funcstring)));vector(length(lis),h,forbarbaz1(lis[h]))
/* Working toward polynomials for unramified extensions */
/* Polynomial mod p is irreducible */
/* Lots of other ways to do this, like polisirreducible(pol*Mod(1,p)) */
isirredmodp(pol, p) = poldegree(pol) == poldegree(factormod(pol,p)[1,1])
/* For an irreducible polynomial mod p: is it primitive? */
isprimpol(pol,p) =
{
local(m,oo, pps);
m=poldegree(pol);
pol=pol*Mod(1,p);
oo=p^m-1; /* Order of the group */
pps=factor(oo)[,1]~; /* primes dividing the order of the group */
for(j=1,length(pps),
if(lift(lift(Mod(x,pol)^(oo/pps[j]))) == 1, return(0)));
return(1);
}
/* Unramified defining polynomial matching the criteria in our paper
The only slightly tricky part is stepping through the proper lex
ordering on polynomials. */
unrampoljr(m, p) =
{
local(trying, pol, j,s);
trying=1;
pol=x^m;
while(trying,
j=0; s=1;
while(polcoeff(pol,j)==(p-1)*s, pol -= s*(p-1)*x^j;s= -s;j++);
pol += s*x^j;
if(isirredmodp(pol, p), if(isprimpol(pol,p), trying=0)));
pol
}
/* Generating tame polynomials */
/* Utility for next function */
removezeros(lis) =
{
local(cnt=0, ans);
for(j=1,length(lis),if(lis[j],cnt++));
ans=vector(cnt);
cnt=0;
for(j=1,length(lis),if(lis[j],cnt++;ans[cnt]=lis[j]));
ans
}
/* For tame routine, try to lift an orbit mod g to one mod p^f-1
of length f */
liftorb(orb,p,f,g) =
{
local(m, v,s,t);
m=p^f-1;
v=vector(f,h,0);
for(k=0,m/g-1,
for(j=1,length(orb),
s=orb[j]+k*g;
v[1]=s;
t=1;s=(v[1]*p) % m;
while(v[1] != s, t++;v[t]=s;s=(v[t]*p) % m);
if(t==f, return(v))));
return(-1);
}
/* Produce a list of tame extensions with the requested data */
tames(p,f,e) =
{
local(unrp, ans, g,v,vorb,k,norb, f2, g2, fm);
if(e==1, unrp=unrampoljr(f,p);
return([[unrp, 0,1,f,1,[],[f,1],[1,1],subst(unrp,x,t),y-p]]));
unrp = subst(unrampoljr(f,p),x,y);
g = gcd(e, p^f-1);
/* Extra degree needed to get roots of 1 */
f2 = lcm(f,znorder(Mod(p,e)))/f;
/* Compute needed orbits */
v=vector(g-1,h,1);
vorb=[[0]];
for(j=1,g-1,
if(v[j]!=0,
norb=[j];
k=(j*p) % g; v[j]=0;
while(k!=j, v[k]=0; norb=concat(norb,[k]); k=(k*p) % g);
vorb=concat(vorb,[norb]);
));
/* print("f = ", f, " g = ", g, " Orbits ", vorb); */
/* Now look for lifts */
v=vector(length(vorb),h,liftorb(vorb[h],p,f,g));
/* print("Lifted to: ", v); */
/* Strategy one */
for(j=1,length(v),
/* Compute extra unramified part for this one */
fm = 1;
gm = gcd(p^(f*f2*fm)-1,e);
while((vorb[j][1]*(p-1)*(p^(f*f2*fm)-1)/(p^f-1) % gm) != 0,
fm++;
gm = gcd(p^(f*f2*fm)-1,e));
/* Now compute the polynomial */
if(v[j]!= -1,
norb = polresultant(x^e-p*y^v[j][1], unrp,y);
norb /= pollead(norb);
v[j] = [norb, f*f2*fm, v[j][1]],
/* Root shift procedure */
norb = polresultant((x+y)^e-p*y^vorb[j][1],unrp,y);
norb /= pollead(norb); /* be safe */
v[j] = [norb, f*f2*fm,vorb[j][1]];
));
for(j=1,length(v),
v[j]= [v[j][1],f*(e-1),e,f,e,[],[e, v[j][2]],[e,1],
subst(unrp,y,t),y^e-p*t^v[j][3]]);
return(v);
}
/* Generate all degree p ramified extensions of Qp */
/* Format is poly, c, e, f, t, slopes, d-galois, d-inert */
padicdegp(p) =
{
local(ans, c, d1, d2, g,m, cnt,unr);
cnt=0;
ans=vector(p^2,h,0);
unr = subst(unrampoljr(1,p),x,t);
for(j=0,p-1,
ans[cnt++]=[x^p+p*(1+j*p), 2*p-1, p, 1, p-1, [(2*p-1)/(p-1)],
[p, p-1], [p,p-1], unr,y^p+p*(1+j*p)]);
for(j=0,p-1,
ans[cnt++]=[x^p-p*x^(p-1)+p*(1+j*p),2*p-2,p,1,1,[2],[p,1],
[p,1],unr,y^p-p*y^(p-1)+p*(1+j*p)]);
for(j=1,p-2,
c = 2*p-2;
g=gcd(p-1,c);
m = znorder(Mod(-j, p));
d2= (p-1)/gcd((p-1)/m, g);
ans[cnt++]=[x^p+j*p*x^(p-1)+p,2*p-2,p,1,1,[2],[p,d2],
[p,1],unr,y^p+j*p*y^(p-1)+p]);
for(j=1,p-1,
for(k=1,p-2,
c = p+k-1;
g=gcd(p-1,c);
m = znorder(Mod(k*j, p));
d1=(p-1)/g;
d2= (p-1)/gcd((p-1)/m, g);
ans[cnt++]=[x^p+j*p*x^k+p,c,p,1, d1, [c/(p-1)],
[p,d2],[p,d1],unr,y^p+j*p*y^k+p]));
ans
}
/* Put these together for easier access */
quickdegree(p,n) =
{
local(pls=[],unrp);
if(p==n,
unrp = unrampoljr(p,p);
pls = concat([[unrp,0,1,p,1,[],[p,1],[1,1],subst(unrp,x,t),y-p]],
padicdegp(p)),
dn=divisors(n);
for(j=1,length(dn), pls = concat(pls, tames(p,dn[j],n/dn[j])));
);
pls
}
/* Start of conversion routines from the simple format used for dynamic
entries to the format used for wild extensions of composite degree.
Inputs are the single entry, and the prime p. So if you have
p=5; n=6
mylist = quickdegree(p,n)
Then you would need
mylist2 = mapz("conv1(z, p)", mylist)
to get the list.
*/
/* For easy data */
conv1(ent, p) =
{
local(ent2, pdc,uu);
pdc=pdiscclass(poldisc(ent[1]), p);
uu = ent[7][1]*ent[7][2]/ent[8][1]/ent[8][2];
ent2= [p, ent[2], ent[3], ent[4], ent[3]*ent[4], ent[1], 0, 0,
0, ent[6], [ent[5], uu] , [ent[9],ent[10]], 0, pdc, pdc[1], pdc[2],
dohwp(ent[1],p,ent[3]), 0, 0, 0, -1, 0,ent[7], ent[8]];
/* ent[7] and ent[8] are in 23 and 24 */
/* special normalization for sorting on 1st part of disc field */
if(p==2 && ent2[15]<0, ent2[15] *= -3/2);
ent2[21] = slopeave(p, ent[5], ent[6]); /* compute GMS */
/* ent2[22] = countroots(ent2[6], ent2[6], p, ent2[12]); automorphisms */
return(ent2);
}
/* Take a discriminant and a prime p and give the local discriminant
root field in our internal format */
pdiscclass(disc,p) =
{
local(tdis, expo);
tdis = disc;
expo=valuation(tdis,p); tdis = tdis/p^expo;
expo = expo % 2;
if(p==2, tdis = tdis % 8, tdis = tdis % p);
if(p==2, if(tdis==1, [p^expo, 0], if(tdis==5, [p^expo, 1],
if(tdis == 7, [- p^expo, 0], [- p^expo, 1]))),
[p^expo, if(kronecker(tdis,p)==1,0,1)])
}
/* Wrapper for easier access to pdiscclass from a polynomial and the
prime p */
getdisc(poly, p) = pdiscclass(poldisc(poly), p)
/* Local root numbers */
/* Hasse-Witt invariant for just for one prime p.
Deal with easy cases, otherwise use orthogonalization.
This only gives the adjustment from the invariant for the
quadratic field. See below for the final part.
*/
dohwp(inpol, p, e) =
{
local(n, fudge, delt, f, d);
if((p % 2)==0,
/* Odd degree is easy */
if((poldegree(inpol) % 2) ==1, return((-1)^((e^2-1)/8)),
return(dohw(inpol, [p])[1])));
/* Now p must be odd */
n=poldegree(inpol);
delt = (-1)^(n*(n-1)/2);
d = poldisc(inpol);
fudge = hilbert(delt, d, p);
if((n % 2)==1, return(fudge));
if((e % p) != 0, /* Tame */
if((e % 2)==1, return(fudge));
f = n/e;
if((p % 4) == 1,
if((f % 2) == 0, return(-hilbert(p, d, p)));
return(hilbert(p, e, p))); /* f odd */
/* Now p === 3 (mod 4) */
if((f % 2) == 1, return((-1)^(f*(f+1)/2)*hilbert(p, e, p)*hilbert(d,delt,p)));
return((-1)^(f*(f+1)/2)*hilbert(p*d, p*delt, p)); /* f even */
);
/* Fall back */
return(dohw(inpol, [p])[1])
}
/* dohw(poly, plist) gives a vector of invariants for the list of
primes (code originally written for number fields.
0 represents the infinite place. If you want only one
prime p, then dohw(poly, [p])[1] does the trick.
*/
dohw(inpol, rplist)=inv2(ma(inpol), rplist)
raml(list, rplist) =
{
vector(length(rplist), h, prod(k=1, length(list)-1,
prod(j=k+1, length(list), hilbert(list[k],list[j], rplist[h]))))
}
inv2(m, rplist) = raml(gs(m), rplist)
ma(inpol) = matrix(length(inpol)-1,length(inpol)-1,xx,yy, trace(Mod(x,inpol)^(xx+yy-2)))
gs(m) =
{
local(len, ttmp, gotit);
gotit = gramschmidt(m);
if(gotit == 0, len=length(m);
ttmp = matrix(len,len,xx,yy, (random() % 11)-5);
if(matdet(ttmp), gs(ttmp*m*(ttmp~)), gs(m)), gotit)
}
gramschmidt(m,u,w,ttmp, ok) =
{
u = vector(length(m),j,vector(length(m),k, j==k));
w=u; ok=1;
for(j=1, length(m),
if(j>1 && w[j-1]*m*w[j-1]~ == 0, return(0));
w[j] = u[j]-sum(k=1,j-1,(u[j]*m*w[k]~)/(w[k]*m*w[k]~) *w[k]);
);
w = matrix(length(m),length(m),xx,yy,w[xx][yy]);
ttmp = w*m*(w~);
vector(length(m), j, ttmp[j,j]*denominator(ttmp[j,j])^2)
}
/* To take the local root number from the form it is stored and print
it, we use prinhw. Supporting routines follow it.
Here, p is the prime, invec is the discrim. root field (in the
internal format as a pair), and j is the adjustment value computed
by dohw above. If we have an entry "ent", then the call is
prinhw(ent[1], ent[14], ent[17])
*/
prinhw(p,invec,j) =
{
local(temp);
temp = (1-hilbert(2,myquaddisc(invec,p),p)+ qhw(invec,p)+(1-j)) % 4;
prinhwsimple(temp);
}
/* Converts a quadratic discriminant root field our from internal format
to an integer d s.t. the field is Q_p(sqrt(d)) */
myquaddisc(invec,p) =
{
local(temp);
if(invec[2]==0,
quaddisc(invec[1]),
/* unramified twist */
if(p==2, return(quaddisc(5*invec[1])));
temp=1;
while(kronecker(prime(temp),p) != -1, temp++);
quaddisc(prime(temp)*invec[1]))
}
/* Local root numbers for quadratic fields
The quadratic field comes in the internal format. We also need to know p.
The result is an exponent mod 4 so that it is really (-i)^(this number).
*/
qhw(tdis, p) =
{
local(myval);
if(tdis[1]==1, 0,
if(p<7,
if(p==2,
if(tdis==[2,0], 0,
if((tdis==[-2,0]) || (tdis==[-1,0]) || (tdis==[-1,1]), 3,
if(tdis==[2,1], 2, 1))),
if(p==3, if(tdis==[3,0], 1, 3),
if(tdis==[5,1], 2, 0))),
if((p % 4) == 3,
if(tdis==[p,1], 3, (4-qhw(getdisc(x^2-p,2),2)) % 4),
if(tdis[2]==0, 0, myval=1;
while(kronecker(prime(myval), p) != -1, myval = myval+1);
(4-(1-kronecker(-1,prime(myval)))/2-
qhw(getdisc(x^2-kronecker(-1,prime(myval))
*prime(myval)*p,prime(myval)),prime(myval))) %4))))
}
/* Prints (-i)^temp where i is gp's I, assuming temp is already
reduced mod 4. */
prinhwsimple(temp) =
{
if(temp==0, print1("1"),
if(temp==1, print1("-i"),
if(temp==2, print1("-1"),
print1("i"))));
}
/* Galois mean slope from the prime, the tame degree, and the list
of wild slopes */
slopeave(p, tame, wlist) = ((p-1) * sum(j=1,length(wlist),wlist[j]*p^(j-1))+\
(tame-1)/tame)/p^length(wlist)
/* Utility to search through subfields for field whose nearly full
entry is given by ent, and we look through list of candidates
lis */
findsubs(ent, lis) =
{
local(ans=[]);
for(j=1,length(lis),
if((ent[3] %lis[j][3])==0 && (ent[4] %lis[j][4])==0,
if(countroots(lis[j][6], ent[6], ent[1], ent[12], ent[13],1)>0,
ans=concat(ans, [lis[j][6]]))));
return(ans);
}
/* Determines degrees to check in, then finds the subs, and puts
them in the right format */
findallsubs(ent) =
{
local(divs);
divs = divisors(ent[5]);
vector(length(divs)-2,h,[divs[h+1], findsubs(ent, subdegs[divs[h+1]])])
}
/* Look up a polynomial in a pre-generated list.
We don't try to compute any invariants of poly because it might be
really horrible (e.g., coming from high precision p-adic factorization),
so we take the safer route and just compare to everything.
Returns the matching poly, or 0 if it can't find one.
*/
findinlist(poly, lis) =
{
for(j=1,length(lis),
if(countroots(poly, lis[j][6], lis[j][1], lis[j][12], lis[j][13],1)>0,
return(lis[j][6])));
return(0)
}
/* Utility, if we know a polynomial is in a list, and we want its index */
indexofpoly(poly, lis) = for(j=1,length(lis),if(lis[j][6]==poly,return(j))); 0
global(subdegs);
/* Returns completed list with full format for tame and (p,p) extensions.
Optional parameter is so that we can call recursively to compute
subfield data, but we don't have to keep recomputing it.
*/
doitall(p,n, havesubdegs= 0) =
{
local(sd, mylist, fpf, twinmatches, ent);
sd = divisors(n);
if(length(sd)>2 && havesubdegs==0,
/* don't need n, this has entries we won't fill, but this is easy
to access for all degrees dividing n */
subdegs= vector(n-1,h,[]);
for(j=2, length(sd)-1,
subdegs[sd[j]] = doitall(p, sd[j], 1);
);
);
mylist = quickdegree(p,n);
mylist = mapz("conv1(z, p)", mylist); /* Fast stuff */
/* Fill in automorphisms */
for(j=1,length(mylist),
mylist[j][22] = countroots(mylist[j][6], mylist[j][6], p, mylist[j][12]));
/* Fill in subfields */
for(j=1,length(mylist),
mylist[j][18] = findallsubs(mylist[j]));
/* If the degree is 6, we need to compute, factor, and identify
factors of twin. Here, p > 5, so we only have to worry about factors
of degree <4, which means they divide 6, which means they are already
computed in subdegs. */
if(n==6,
default(realprecision, 500);
for(j=1,length(mylist),
mylist[j][19] = real(mytwin(mylist[j][6]));
fpf = factorpadic(mylist[j][19], p, 500)[,1]~;
fpf = vecsort(mapz("[z, poldegree(z)]", fpf), [2]);
fpf = mapz("lift(Mod(1,p^500)*z[1])", fpf);
twinmatches=[];
for(jj=1,length(fpf),
if(poldegree(fpf[jj])>1, /* skip degree 1 factors */
twinmatches=concat(twinmatches,
findinlist(fpf[jj], subdegs[poldegree(fpf[jj])]));
));
mylist[j][20] = twinmatches;
);
);
/* Now fill in Galois and inertia group entries. For tame and (p,p),
we can use quickgalois fuction (from localgalois.gp).
Some of the data was specially stored in entries 23 and 24 by conv1 */
for(j=1,length(mylist),
ent = mylist[j];
mylist[j][9] = qgal(ent[24], ent, ent[4]>1);
mylist[j][7] = qgal(ent[23], ent);
mylist[j][8] = ent[23][1]*ent[23][2]);
mylist = vecsort(mylist, [2,3,15,16,21,8,22]);
return(mylist);
}
/**********************************************************************/
/**********************************************************************/
/* For fun, Conway polynomials - but not used by db */
/* We cache ones which have been computed so far in conwaylist */
global(conwaylist);
/* A trick to initialize the cache, but only if we have not loaded this
file already - helpful when debugging */
if(type(conwaylist)=="t_POL", conwaylist=[]);
subismult(pow, starter, modu) =
{
local(ans);
ans=Mod(0,modu);
for(j=0, poldegree(starter),
if(lift(polcoeff(starter, j))!= 0,
ans += polcoeff(starter, j)*Mod(x^j, modu)^pow));
lift(lift(ans)) == 0
}
conway(m, p) =
{
local(trying, pol, j, divs,subcon,try2);
for(j=1,length(conwaylist),
if(conwaylist[j][1] == [m,p], return(conwaylist[j][2])));
trying=1;
/* Degree 1 is basically primitive roots */
if(m==1, return(Mod(1,p)*x-znprimroot(p)));
pol=x^m* Mod(1,p);
divs = factor(m)[,1]~;
subcon = vector(length(divs), h, conway(m/divs[h],p)*Mod(1,p));
while(trying,
j=0;
while(lift((-1)^(m-j+1)*polcoeff(pol,j))==1,
pol -= (-1)^(m-j)*(p-1)*x^j;j++);
if(j==m, return("failed"));
pol += (-1)^(m-j)*x^j;
if(isirredmodp(pol, p), if(isprimpol(pol,p),
try2=1;
while(00, trying=0))));
conwaylist = concat(conwaylist, [[[m,p], pol]]);
pol
}
/* End of Conway poly section */