function X_1_n(n,base_ring : equation_directory:="models_X1_n") //Input: n - integer // base_ring - a ring // equation_directory - directory with files FFFc.txt containing models //Output: C - a curve //Returns an algebraic model C of the modular curve X_1(m,n) as a curve over base_ring n_str := IntegerToString(n); file_name := equation_directory cat "/FFFc" cat n_str cat ".txt"; data := Read(file_name); A := AffineSpace(base_ring,2); X := eval(data); C := Curve(A,X); return ProjectiveClosure(C); end function; function Functions_xyrsbcF2F3(curve) //Input: curve - the modular curve X_1(N) as returned by the function X_1 //Output: x,y,r,s,b,c,F2,F3 - The modular units as in http://arxiv.org/pdf/1307.5719v1.pdf FF := FunctionField(curve); x := FF.1; y := FF.2; r := (x^2*y-x*y+y-1)/x/(x*y-1); s := (x*y-y+1)/x/y; b := r*s*(r-1); c := s*(r-1); F3 := b; F2 := b/(16*b^2+(1-20*c-8*c^2)*b + c*(c-1)^3); return x,y,r,s,b,c,F2,F3; end function; function X_1_n_jInvariant(curve) x,y,r,s,b,c,F2,F3 := Functions_xyrsbcF2F3(curve); jNum := 4096*b^6 - 6144*b^5*c^2 - 6144*b^5*c + 12288*b^5 + 3840*b^4*c^4 + 3072*b^4*c^3 - 4608*b^4*c^2 - 15360*b^4*c + 13056*b^4 - 1280*b^3*c^6 + 768*b^3*c^5 + 1536*b^3*c^4 - 2048*b^3*c^3 + 8448*b^3*c^2 - 13056*b^3*c + 5632*b^3 + 240*b^2*c^8 - 768*b^2*c^7 + 384*b^2*c^6 + 384*b^2*c^5 + 2400*b^2*c^4 - 7680*b^2*c^3 + 8448*b^2*c^2 - 4224*b^2*c + 816*b^2 - 24*b*c^10 + 168*b*c^9 - 432*b*c^8 + 288*b*c^7 + 1008*b*c^6 - 3024*b*c^5 + 4032*b*c^4 - 3168*b*c^3 + 1512*b*c^2 - 408*b*c + 48*b + c^12 - 12*c^11 + 66*c^10 - 220*c^9 + 495*c^8 - 792*c^7 + 924*c^6 - 792*c^5 + 495*c^4 - 220*c^3 + 66*c^2 - 12*c + 1; jDen := 16*b^5 - 8*b^4*c^2 - 20*b^4*c + b^4 + b^3*c^4 - 3*b^3*c^3 + 3*b^3*c^2 - b^3*c; return jNum/jDen; end function; function TateNormalForm_bc(E,P); //Return the b,c of the tate normal form of (E,P) as in equation (2) of http://arxiv.org/pdf/1307.5719v1.pdf assert P[3] eq 1; x0:=P[1]; y0:=P[2]; a1,a2,a3,a4,a6:=Explode(aInvariants(E)); aa1:=a1; aa3:=2*y0+a3+a1*x0; aa2:=3*x0+a2; aa4:=3*x0^2+2*x0*a2+a4-a1*y0; aaa1:=2*aa4/aa3+aa1; aaa3:=aa3; aaa2:=aa2-(aa4/aa3)^2-aa1*aa4/aa3; b:=-aaa2^3/aaa3^2; c:=-(aaa1*aaa2-aaa3)/aaa3; return [b,c]; end function; function TateNormalForm_xy(E,P); //return the x,y of the tate normal form of (E,P) as in section 2.1 http://arxiv.org/pdf/1307.5719v1.pdf b,c := Explode(TateNormalForm_bc(E,P)); r := b/c; s := c^2/(b-c); t := (r*s-2*r+1); x := (s-r)/t; y := t/(s^2-s-r+1); return [x,y]; end function; function EllipticCurveFromX1Place(P); //Returns the associated elliptic curve corresponding to a place on X1N //the elliptic curve is guaranteed to be in tate normal form, so that //0,0 is the point of order N. The point 0,0 is returned as optional second element X1N := Curve(P); x,y,r,s,b,c,F2,F3:=Functions_xyrsbcF2F3(X1N); bP:=Evaluate(b,P); cP:=Evaluate(c,P); E:=EllipticCurve([1-cP,-bP,-bP,0,0]); return E, E ! [0,0]; end function; function X1PlaceFromEllipticCurve(X1N, E, P) //Returns a place on X_1(N) given an elliptic curve and a point of order N K := BaseRing(E); xy := TateNormalForm_xy(E,P); dP := Places(X1N(K) ! xy); assert #dP eq 1; return dP[1]; end function; function ElementsUpToFrobenius(F) orbits := {{Frobenius(x,i) : i in [1..Degree(F)]}: x in F}; return [Random(orbit) : orbit in orbits]; end function; function EllipticCurvesOverField(F) return &cat[Twists(EllipticCurveFromjInvariant(j)) : j in F]; end function; function EllipticCurvesOverFieldUpToFrobenius(F) return &cat[Twists(EllipticCurveFromjInvariant(j)) : j in ElementsUpToFrobenius(F)]; end function; function EllipticCurvesWithPointOverFieldUpToFrobeniusAndDiamond(p,i,N) assert IsSquarefree(N); F := GF(p,i); ECs := [E: E in EllipticCurvesOverFieldUpToFrobenius(F) | (#E(F) mod N) eq 0]; ECs_with_point := []; for E in ECs do gens := [P*(Order(P) div N) : P in Generators(E(F)) | (Order(P) mod N) eq 0]; assert #gens eq 1; Append(~ECs_with_point,); end for; return ECs_with_point; end function; function NonCuspidalPlacesUpToDiamond(C,i,N) assert IsSquarefree(N); p := Characteristic(BaseRing(C)); F := GF(p,i); ECs := [E: E in EllipticCurvesOverFieldUpToFrobenius(F) | (#E(F) mod N) eq 0]; places := []; for E in ECs do gens := [P*(Order(P) div N) : P in Generators(E(F)) | (Order(P) mod N) eq 0]; assert #gens eq 1; time Append(~places,X1PlaceFromEllipticCurve(C,E,gens[1])); end for; return places; end function;