\\ Copyright (c) 2003 Henri Darmon and Adam Logan /* Some code that supports finding a_p for elliptic curves defined over quadratic fields, and some general nonsense. */ /* First, a little snippet to find a quadratic nonresidue. Just tries things until we find one. Input: p and f, an allegedly irreducible polynomial mod p. Output: a polymod giving a nonsquare element of the field Z[x]/(p,f). */ nonres(p,f)= { local(d,v); if(p==2,error("everything's a square in that field"),); v= (mattranspose(factormod(f,p))); if (length(v)>1 || v[2,1]>1,error("reducible polynomial in nonres"),); d=poldegree(f); while(1,try=Mod(sum(i=0,d-1,variable(f)^i*Mod(random(p),p)),f);if(try^((p^d-1)/2)==p-1,break,)); return(try) } addhelp(nonres,"nonres(p,pol): assumes that pol is irreducible mod p, and returns a nonsquare in the field Z[x]/(p,f)"); /* Taking square roots in finite fields, given a quadratic nonresidue (see above). Is this called Coppersmith's algorithm? Parameters are polymods giving the element of the field and a nonresidue, and the order of the field (just for convenience); return value is the square root, if there is one. */ sqrff(sq, nr, q)= { local(n,r,s,guess,i); if (Mod(q,2),,return(sq^(q/2))); /* char 2 is easy (if not useful)*/ if (sq == 0, return(0),); if (sq^((q-1)/2)-1,error("not a square in sqrff",)); r=valuation(q-1,2);s=(q-1)/2^r; n= nr^s; guess = sq^((s+1)/2); /* Now we have n, an element of order r, the highest power of 2 that divides the order of the group of units. We also have an initial guess for what the square root might be; it's off by a power of n. The method now is to adjust our guess at each step to bring down the order, if necessary. We know that a^s has order dividing 2^(r-1); if it is 2^(r-1), we need to adjust the guess by multiplying it by n. Then we're off by something of order dividing 2^(r-2), etc. */ forstep(i=r-1,1,-1, if((guess^2/sq)^(2^(i-1))-1,guess=guess*n^(2^(r-i-1)),)); if(guess^2 != sq, error("Bug in sqrff!"),return(guess)); } addhelp(sqrff,"sqrff(square,nonres,q): given square, a polymod in a finite field which should be a square, nonres, a nonsquare in the same field (use the nonres() function to find if necessary), and q, the order of a the field, finds a square root of the square"); /* The baby-step-giant-step method for finding the order of a point on an elliptic curve. Input is the curve, the point, and the order of the field. Output, with luck, is the order. */ bsgs(ec,pt,q)= { local(wub,steps,babysteps,newstep,giantstep,giant,v); if(length(pt)==1,return(1),); if(pt[2]==0,return(2),); /* Now we're done with the trivial cases. First priority is to compute the Weil bound, then figure out how many steps we need. */ wub = q + 1 + floor(2*sqrt(q)); steps = ceil(sqrt(wub)); babysteps=vector(steps); newstep=pt; babysteps[1]=vectorize(pt,1); for(i=2,steps,newstep=elladd(ec,newstep,pt);if(newstep == [0],return(i),);babysteps[i]=vectorize(newstep,i)); /* Now the vector babysteps contains the first so-many multiples of the point, and if we've found the order already we can go home. Time to sort it. Note that newstep retains steps * initial point. */ babysteps = vecsort(babysteps,,2); /* And now for the giant steps. */ giantstep=ellpow(ec,newstep,-1);giant=[0]; for(i=1,steps, giant=elladd(ec,giant,giantstep); if(giant==[0],return(i*steps),); v=vectorize(giant)[1]; if(found=search(v,babysteps),return(i*steps+found),)); /* Something's wrong if we haven't yet found it. */ error("Bug in bsgs routines."); } addhelp(bsgs,"bsgs(ec,point,q): finds the order of a point on an elliptic curve over a finite field (where q is the order of the field)"); /* Sample timing: for a point on a curve over F_1019^2, the naive algorithm to find the order took 18.4 seconds, this took 0.5. */ /* Now the real program, at last. Takes a 5-component vector giving the coefficients (of course, this breaks if a1 or a3 is not 0--shh!), a nonresidue to twist by, and, for convenience again, the polynomial defining the field and the residue characteristic. Output should, with luck, be the order of the group of points on the curve. Uses the Shanks-Mestre algorithm with baby-step-giant-step, and tacitly assumes that p is not 2. Here I try a trick that should speed things up a lot in practice. The reality is that a randomly chosen point probably has order N_E/k for some k a lot smaller than the order of the field, so...well, read the code. */ ellaq(e,n,f,p,slow=0)= { local(d,curves,orders,tryx,tryy,curvept,a,wlb); d=poldegree(f); if(p^d<100,return(trivialellaq(e,f,p,d)),); curves=[ellinit(e),ellinit(elltwist(e,n))]; orders=[1,1]; wlb=p^d+1-2*sqrt(p^d); while(1, tryx=Mod(sum(i=0,d-1,Mod(random(p),p)*x^i),f); tryy=tryx^3+e[2]*tryx^2+e[4]*tryx+e[5]; /* We've chosen (x,f(x)) now. If f(x) = 0, we get a 2-torsion point on both the curves; deal appropriately. Otherwise, we get a curve-and-point, either the original or the twisted one. Find its order and adjust. */ if(tryy,if(tryy^((p^d-1)/2)+1,curvept=[1,[tryx,sqrff(tryy,n,p^d)]],curvept=[2,[tryx*n,sqrff(tryy*n^3,n,p^d)]]);orders[curvept[1]]=lcm(orders[curvept[1]],if(slow,bsgs(curves[curvept[1]],curvept[2],p^d),fastorder(curves[curvept[1]],curvept[2],p^d))),orders=lcm(orders,2)); a=lcm(orders[1],orders[2]); if(a>4*sqrt(p^d),cong=chinese(Mod(0,orders[1]),2*Mod(p^d+1,orders[2]));t=floor(wlb/a)*a+lift(cong);if(t