_grp_ell_ops := rec();
_grp_ell_ops.Print :=function(r)
          if Length(r.L)= 2 then
            Print("EllipticCurve over ",r.F," defined by  y^2 = x^3 ");
            if r.L[1]<> 0 then Print("+ ",r.L[1],"*x "); fi;
            if r.L[2]<> 0 then Print("+ ",r.L[2]); fi;
            Print("\n");
          else Print("EllipticCurve over ",r.F," defined by  y^2 ");
            if r.L[1]<> 0 then Print("+ ",r.L[1],"*x*y "); fi;
            if r.L[2]<> 0 then Print("+ ",r.L[2],"*y "); fi;
            Print(" = x^3 ");
            if r.L[3]<> 0 then Print("+ ",r.L[3],"*x^2 ");fi;
            if r.L[4]<> 0 then Print("+ ",r.L[4],"*x "); fi;
            if r.L[5]<> 0 then Print("+ ",r.L[5]); fi;
            Print("\n");
	  fi;
	    end;

_grp_ell_ops.\=:= function(EC1,EC2)
         local L1,L2;
         if EC1.F<>EC2.F then return FALSE;
	 fi;
	 L1:=EC1.L;
	 L2:=EC2.L;
	 if Length(L1) < Length(L2) then
	    if L2[1] <> 0 then return FALSE;
	    elif L2[2] <> 0 then return FALSE;
	    elif L2[3] <>0 then return FALSE;
	 fi;
	 fi;
	 return L1 = L2;
end;

_elt_grp_ell_ops:= rec();



_NewE:=function()
ReadLib("elliptic");
Print("\n");
end;

_Bin := function (n)
local i,L;
i:= 0;
L:= [];
while n>0 do
   L[i+1] := n mod 2;
   n := Div(n,2);
   i:= i+1;
od;
return L;
end;

X:= function(P)
if P = Zero(Parent(P)) then return INFTY;
else
return P.x;
fi;
end;

Y:= function(P)
if P = Zero(Parent(P)) then return INFTY;
else
return P.y;
fi;
end;

_WNF:= function(F,L)
local a1,a3,a2,a4,a6,b2,b4,b6;
   a1:= L[1];
   a3:= L[2];
   a2:= L[3];
   a4:= L[4];
   a6:= L[5];
   b2:=a1^2+4*a2;
   b4:= 2*a4+a1*a3;
   b6:= a3^2+4*a6;
return [b2,b4,b6];
end;

_WeierstrassNormalForm := function(F,L)
local a1,a2,a3,a4,a6,b2,b4,b6,A,B,c4,c6;
if Length(L) = 2 then return [F,L]; fi;
if Characteristic(F) <5 then
    return Error("Characteristic of base field is 2 or 3, cannot create WeierstrassNormalForm\n");
fi;
   a1:= L[1];
   a3:= L[2];
  a2:= L[3];
   a4:= L[4];
   a6:= L[5];
   b2:=a1^2+4*a2;
   b4:= 2*a4+a1*a3;
   b6:= a3^2+4*a6;
   c4:= b2^2-24*b4;
   c6:= -b2^3+36*b2*b4-216*b6;
   A:= -27*c4;
   B:= -54*c6;
return [F,[Coerce(F,A),Coerce(F,B)]];
end;

_Discriminant:= function(F,L)
local b2,b4,b6,b8,a1,a3,a2,a4,a6;
if Length(L) = 5 then
   a1:= L[1];
   a3:= L[2];
   a2:= L[3];
   a4:= L[4];
   a6:= L[5];
   b2:= a1^2+4*a2;
   b4:= 2*a4 +a1*a3;
   b6:= a3^2 +4*a6;
   b8:= (a1^2)*a6 +4*a2*a6-a1*a3*a4+a2*(a3^2)-a4^2;
   return (-(b2^2)*b8-8*(b4^3)-27*(b6^2)+9*b2*b4*b6);
else
   return (-16*(4*(L[1]^3)+27*(L[2]^2)));
fi;
end;

ChangeBaseRing:=function(E,F)
return EllipticCurve (F,[Coerce(Integers(),E.L[1]),Coerce(Integers(),E.L[2])]);
end;

_jInv:= function(F,L)
local EWei,LWei,c4;
if Characteristic(F)>3 then
   if Length(L) = 2 then
      LWei:=L;
   else
      EWei:= _WeierstrassNormalForm(F,L);
      LWei:= EWei[2];
   fi;
   return 1728*((4*(LWei[1]^3))/(4*(LWei[1]^3)+27*(LWei[2]^2)));
else
   LWei:= _WNF(F,L);
   c4:= LWei[1]^2-24*LWei[2];
   return c4^3/_Discriminant(F,L);
fi;
end;

_Discriminant2:= function(EC)
return _Discriminant(EC.F,EC.L);
end;

ShortWeierstrassNormalForm:= function(EC)
local L;
if Characteristic(EC.F) >3 then
L:=_WeierstrassNormalForm(EC.F,EC.L);
return EllipticCurve(L[1],L[2]);
else return Error("Characteristic of base field is 2 or 3, Short WNF cannot be computed  \n");
fi;
end;


IsSupersingular:= function(E)
local f,eltseq,p,BR,PA,E2,m;
BR:= BaseRing(E);
p:= Characteristic(BR);
if p = 2 then
   if E.L[3]=0 and E.L[4]=0 and E.L[5] = 0 then
      return true;
   else return false;
   fi;
else
   PA:= PolynomialAlgebra(BR);
   if Length(E.L) = 2 then
      E2:= E;
   else E2:=ShortWeierstrassNormalForm(E);
   fi;
   m:= Coerce(Integers(),((p-1)/2));
   f:= Coerce(PA,[E2.L[2],E2.L[1],0,1])^m;
   eltseq:= Eltseq(f);
   if eltseq[p] = 0 then return true;
   else return false;
   fi;
fi;
end;


jInvariant:= function(EC)
return _jInv(EC.F,EC.L);
end;



_PointMult := function(n,P)
local Li,M,Q,i,j,y1,L;
if P.x = "INFTY POINT" then return P; fi;
if n = 0 then
   return rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
fi;
if n <0 then
   n := -n;
   Li:= P.parent.L;
   if Length(Li)=2 then
      P := rec (x := P.x, y := -P.y, parent := P.parent, type := elt-grp^ell, operations:=_elt_grp_ell_ops);
   else
      y1:=(-Li[1]*P.x-Li[2]-P.y);
      P := rec (x := P.x, y := y1, parent := P.parent, type := elt-grp^ell,operations:=_elt_grp_ell_ops);
   fi;
fi;
L:= _Bin(n);
M:= [P];
j := 1;
for i in [2..Length(L)] do
    M[i]:= M[i-1]+M[i-1];
    od;
while L[j] = 0 do
      j := j+1;
      od;
Q := M[j];

for i in [(j+1)..Length(L)] do
    if L[i] = 1 then
       Q :=  M[i]+Q;
    fi;
od;
return Q;
end;

_elt_grp_ell_ops.\*:= function(n,P) return _PointMult(n,P); end;

_Parent:= function(P)
return P.parent;
end;

_GetPoly:= function(F,L)
local kx,kxy,f,i,x,M;
kx := PolynomialAlgebra (F);
kxy:= PolynomialAlgebra (kx);
x:= Coerce(kxy,kx.1);
M:=[];
for i in [1..Length(L)] do
    M[i]:=Coerce(kxy,L[i]);
od;
if Length(L) = 2 then
    f:= kxy.1^2 - x^3 -(M[1])* x -M[2];
else
    f := (kxy.1^2) +M[1]*(kx.1)*(kxy.1) + M[2]*kxy.1- (kx.1^3) - M[3]*(kx.1^2) - M[4]*kx.1 -M[5];
fi;
return f;
end;

_GetFunf:= function(F,L)
return FunctionField(_GetPoly(F,L));
end;

EllipticFunctionField := function(EC)
local L,kx2,kxy2,f;
L:= EC.L;
kx2:= RationalFunctionField(BaseRing(EC));
kxy2:= PolynomialAlgebra(kx2);
f:= _GetPoly(BaseRing(EC),L);
f:= Coerce(kxy2,f);
return FunctionField(f);
end;

_OldFunf:= function(EC)
local f;
f:= _GetPoly(BaseRing(EC),EC.L);
return FunctionField(f);
end;


_Zero:= function(EC)
return rec(x := "INFTY POINT",parent:= EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
end;

_AddShort := function(P,Q)
local x3, y3,m;
if P.x=Q.x then
    if P.y <> Q.y then
        return rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
    elif (P.y = Q.y and P.y = 0) then
        return rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
    fi;
fi;
if P=Q then
    m:= Coerce(P.parent.F,(3*(P.x)^2+P.parent.L[1])/(2*P.y));
    x3 := m^2 -2*P.x;
    y3 := m*(P.x-x3)-P.y;
else
    m := Coerce(P.parent.F,(Q.y-P.y)/ (Q.x - P.x));
    x3 := m^2 - P.x -Q.x;
    y3 := m*(P.x -x3) - P.y;
fi;

return rec(x:= Coerce(P.parent.F,x3),y := Coerce(P.parent.F,y3), parent := P.parent, type := elt-grp^ell, operations :=_elt_grp_ell_ops);
end;

_AddLong := function(P,Q)
local l, v,L,x1,x2,y1,y2,x3,y3;
x1 := P.x;
x2 := Q.x;
y1 := P.y;
y2 := Q.y;
L:= P.parent.L;

if x1 = x2 then
   if (y1 + y2 + L[1]*x2+L[2] = 0) then
       return rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
   fi;
   l := (3*(x1^2) +2*L[3]*x1+L[4]-L[1]*y1)/(2*y1+L[1]*x1+L[2]);
   v := (-(x1^3) +L[4]*x1+2*L[5]-L[2]*y1)/(2*y1+L[1]*x1+L[2]);
else
    l := (y2-y1)/(x2-x1);
    v := (y1*x2-y2*x1)/(x2-x1);
fi;
x3 := l^2+L[1]*l-L[3]-x1-x2;
y3 := -(l+L[1])*x3 -v -L[2];
return rec(x:= Coerce(P.parent.F,x3),y := Coerce(P.parent.F,y3), parent := P.parent, type := elt-grp^ell, operations :=_elt_grp_ell_ops);
end;



_elt_grp_ell_ops.\+:= function(P,Q)
local x3, y3,m;
if ((P.parent) <> (Q.parent)) then
   Print("Cannot add Points on different curves\n");
   return;
elif P.x = "INFTY POINT" then return Q;
elif Q.x = "INFTY POINT" then return P;
fi;
if Length(Q.parent.L) =2 then return _AddShort(P,Q);
else return _AddLong(P,Q);
fi;
end;




_elt_grp_ell_ops.\=:= function(P,Q)
if Q.parent <> P.parent then
    return FALSE; fi;
if Q.x = "INFTY POINT" then
   if P.x <> "INFTY POINT" then
   return FALSE;
   else return TRUE;
   fi;
fi;
if P.x = "INFTY POINT" then return FALSE;
else return (Q.x = P.x and Q.y = P.y);
fi;
end;




_elt_grp_ell_ops.Print :=function(r)
  if r.x = "INFTY POINT" then
    Print("INFTY POINT");
  else Print("[",r.x,",",r.y,"]");
  fi;
end;



_SwapVars := function (f)
local poly, listpol1,listpol2,kx,kxy,l,g;
poly := 0;
kxy := Parent(f);
kx := CoefficientRing(kxy);
listpol1 := List(f);
listpol2 := [];
for l in [1..Length(listpol1)] do
    if listpol1[l] = 0 then
       listpol2[l] := 0;
    else listpol2[l] := List(listpol1[l]);
    fi;
od;
for l in [1..Length(listpol1)] do
    listpol2[l]:=Coerce(kxy,listpol2[l]);
od;
for l in [1..Length(listpol1)] do
    poly := poly + (listpol2[l])*kx.1^(l-1);
od;
return poly;
end;





_Discriminant:= function(F,L)
local LWei,b8,a1,a3,a2,a4,a6,b2,b4,b6;
if Length(L)=2 then
   return -16*(4*L[1]^3+27*L[2]^2);
fi;
LWei:= _WNF(F,L);
   a1:= L[1];
   a3:= L[2];
   a2:= L[3];
   a4:= L[4];
   a6:= L[5];
b2:=LWei[1];
b4:= LWei[2];
b6:= LWei[3];
b8:= a1^2*a6+4*a2*a6-a1*a3*a4+a2*a3^2-a4^2;
return -b2^2*b8-8*b4^3-27*b6^2+9*b2*b4*b6;
end;

ExistsEllipticCurve:= function(p,L)
local i,F,Funf,g,t;
if (Length(L)<>2 and Length(L)<>5) then
    return false;
else
    t := Type(p);
    if Is(t,fld) then
       if IsRec(p) then F:= p.base;
       else F:=p;
       fi;
    elif IsPrimePower(p) = FALSE then
         return false;
    else
    F := FiniteField(p);
    fi;
    for i in [1..Length(L)] do
        L[i] := Coerce(F,L[i]);
    od;
    if _Discriminant(F,L) = 0 then return false; fi;
    Funf := _GetFunf(F,L);
    g := Genus(Funf);
    if g<>1 then return false;fi;
    return rec(base:=true,ext1:=rec(F:= F,L:= L,size:=-1,type := grp^ell,operations :=_grp_ell_ops ));
fi;
end;


EllipticCurve := function (p,L)
local i,F,Funf,g,t;
if (Length(L)<>2 and Length(L)<>5) then
    Print("Length of defining list must be 2 or 5\n");
    return;
else
    t := Type(p);
    if Is(t,fld) then
       if IsRec(p) then F:= p.base;
       else F:=p;
       fi;
    elif IsPrimePower(p) = FALSE then
             Print("first argument must be a prime power\n");
         return;
    else
    F := FiniteField(p);
    fi;
    for i in [1..Length(L)] do
        L[i] := Coerce(F,L[i]);
    od;
    if _Discriminant(F,L) = 0 then Print("Discriminant of defining polynomial is 0\n"); return; fi;
    Funf := _GetFunf(F,L);
    g := Genus(Funf);
    if g<>1 then return Error("Genus of FunctionField is ",g,".\n");fi;
    return rec(F:= F,L:= L,size:=-1,type := grp^ell,operations :=_grp_ell_ops );
fi;
end;




#WeierstrassNormalForm := function(EC)
#if Characteristic(EC.F) = 2 then
#   Print("cannot create short WeierstrassNormalForm\n");
#   return;
#elif Characteristic(EC.F) = 3 then
#   Print("char=3, cannot create complete short WeierstrassNormalForm");
#fi;
#return EllipticCurve(_WeierstrassNormalForm(EC.F,EC.L)[1],_WeierstrassNormalForm(EC.F,EC.L)[2]);
#end;


_EvalPlace := function(r,g)
local kx,kxy,l,lis,i;
kxy := Parent(DefiningPolynomial(Parent(g)));
kx := CoefficientRing(kxy);
l := List(g);
lis := [];
for i in [1.. Length(l)] do
    lis[i] := Evaluate(l[i],r);
od;
return Coerce(kx, lis);
end;



_elt_grp_ell_ops.\-:= function(P,Q)
local S;
if Q.x = "INFTY POINT" then
   S:= Copy(Q);
else S := _PointMult(-1,Q);
fi;
return P+S;
end;

Point:= function(pl,EC)
local L,Funf,kx,kxy,gen,g,r1,r2,i,lis,k,poly,eval1,eval2,f,x,y;
if Degree(pl)<>1 then return Error("Degree of given place is not 1."); fi;
kx := PolynomialAlgebra(EC.F);
kxy := PolynomialAlgebra(kx);
if IsFinite(pl) = FALSE then
      return rec(x := "INFTY POINT",parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
else
      g:=Generators(Ideal(pl));
      r1:=Roots(Coerce(kx,g[1]))[1][1];
      lis :=List(g[2]);
      eval1 := FALSE;
      eval2 := FALSE;
      f:=DefiningPolynomial(_OldFunf(EC));
      for k in [1..Length(lis)] do
             if (Degree(Denominator(lis[k])) > 0 and eval1 = FALSE)  then
                eval1 := TRUE;
                poly := _SwapVars(f);

             elif (Degree(lis[k])>0 and eval2 = FALSE) then
                eval2 := TRUE;
                poly := _EvalPlace(r1,g[2]);
             fi;
      od;
      if eval1 = TRUE then
          r2 :=Roots(Evaluate (poly,r1))[1][1];
      elif eval2 = TRUE then
          r2 := Roots(poly)[1][1];
      else
         r2 :=Roots(Coerce(kxy,lis))[1][1];
      fi;
      x:=r1;
      y:=Coerce(CoefficientRing(kx),r2);
      if not Evaluate(Evaluate(f,y),x) = Element(EC.F,0) then
         return Error("Given place does not belong to given curve");
      fi;
      return rec(x:=x,y:=y,parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
   fi;
end;

Points := function (EC)
local L,Funf,kx,kxy,gen,p,g,r1,r2,i,retL,lis,k,poly,eval1,eval2;
if (Type(EC) <> grp^ell) then
    Print(Type(EC),"\n");
    Print("usage : Points (<grp^ell>);\n");
    return;
fi;
Funf := _OldFunf(EC);
p:=Places(Funf,1);
kx := PolynomialAlgebra(EC.F);
kxy := PolynomialAlgebra(kx);
retL := [];
for i in [1..Length(p)] do
   if IsFinite(p[i]) = FALSE then
      retL[i] :=rec(x := "INFTY POINT",parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
   else
      g:=Generators(Ideal(p[i]));
      r1:=Roots(Coerce(kx,g[1]))[1][1];
      lis :=List(g[2]);
      eval1 := FALSE;
      eval2 := FALSE;
      for k in [1..Length(lis)] do
             if (Degree(Denominator(lis[k])) > 0 and eval1 = FALSE)  then
                eval1 := TRUE;
                poly := _SwapVars(DefiningPolynomial(Funf));

             elif (Degree(lis[k])>0 and eval2 = FALSE) then
                eval2 := TRUE;
                poly := _EvalPlace(r1,g[2]);
             fi;
      od;
      if eval1 = TRUE then
          r2 :=Roots(Evaluate (poly,r1))[1][1];
      elif eval2 = TRUE then
          r2 := Roots(poly)[1][1];
      else
         r2 :=Roots(Coerce(kxy,lis))[1][1];
      fi;
      retL[i]:= rec(x:=r1,y:=Coerce(CoefficientRing(kx),r2),parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
   fi;
od;
return retL;
end;

_Place:=function(P)
local f1,f2,Funf,par,x,y,G,o,Id,kx,f,kxy,Pl,pl,i,kx2,kxy2;
par := Parent(P);
Funf:=_OldFunf(par);
x:= P.x;
if x= "INFTY POINT" then
   o:=EquationOrderInfinite(Funf);
   G:= Decomposition(o);
   return Place(G[1]);
fi;
y:=P.y;
kx:= PolynomialAlgebra(par.F);
kxy:= PolynomialAlgebra(kx);
kx2:= BaseField(Funf);
kxy2:= PolynomialAlgebra(kx2);
f1:= MinimalPolynomial(Coerce(par.F,x));
G:= Generator(Funf,1);
f2:= G-y;
f2:= Coerce(Funf,f2);
f1:= Coerce(CoefficientRing(Funf),f1);
o:= EquationOrderFinite(Funf);
IsMaximal(o);
Id:= Ideal(o,[f1,f2]);
if (IsPrime(Id)) then
    pl:= Place(Id);
else f:= Factorization(Id);
    Pl:= [];
    for i in [1..Length(f)] do
        Pl[i] := Place(f[i][1]);
	if Point(Pl[i],par) =P then
	    pl:= Pl[i]; i:= Length(f)+1;
	fi;
    od;
fi;
return pl;
end;




RandomPoint:= function(EC)
local P,F,kx,r1,r2,g,lis,eval1,eval2,k,poly,Funf,kxy,finished;
Funf := _OldFunf(EC);
finished := FALSE;
while finished = FALSE do
P := RandomPlace(Funf,1);
P:= P.ext1;
kx := PolynomialAlgebra(EC.F);
kxy := PolynomialAlgebra(kx);
if IsFinite(P) then finished := TRUE;
     g:=Generators(Ideal(P));
      r1:=Roots(Coerce(kx,g[1]))[1][1];
      lis :=List(g[2]);
      eval1 := FALSE;
      eval2 := FALSE;
      for k in [1..Length(lis)] do
             if (Degree(Denominator(lis[k])) > 0 and eval1 = FALSE)  then
                eval1 := TRUE;
                poly := _SwapVars(DefiningPolynomial(Funf));

             elif (Degree(lis[k])>0 and eval2 = FALSE) then
                eval2 := TRUE;
                poly := _EvalPlace(r1,g[2]);
             fi;
      od;
      if eval1 = TRUE then
          r2 :=Roots(Evaluate (poly,r1))[1][1];
      elif eval2 = TRUE then
          r2 := Roots(poly)[1][1];
      else
         r2 :=Roots(Coerce(kxy,lis))[1][1];
      fi;
return rec(x:=r1,y:=Coerce(CoefficientRing(kx),r2),parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
fi;
od;
end;

RandomPointWithInf:= function(EC)
local P,F,kx,r1,r2,g,lis,eval1,eval2,k,poly,Funf,kxy;
Funf := _OldFunf(EC);
P := RandomPlace(Funf,1);
P:= P.ext1;
kx := PolynomialAlgebra(EC.F);
kxy := PolynomialAlgebra(kx);
if (IsFinite(P) = FALSE) then
      return rec(x := "INFTY POINT",parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
else
     g:=Generators(Ideal(P));
      r1:=Roots(Coerce(kx,g[1]))[1][1];
      lis :=List(g[2]);
      eval1 := FALSE;
      eval2 := FALSE;
      for k in [1..Length(lis)] do
             if (Degree(Denominator(lis[k])) > 0 and eval1 = FALSE)  then
                eval1 := TRUE;
                poly := _SwapVars(DefiningPolynomial(Funf));

             elif (Degree(lis[k])>0 and eval2 = FALSE) then
                eval2 := TRUE;
                poly := _EvalPlace(r1,g[2]);
             fi;
      od;
      if eval1 = TRUE then
          r2 :=Roots(Evaluate (poly,r1))[1][1];
      elif eval2 = TRUE then
          r2 := Roots(poly)[1][1];
      else
         r2 :=Roots(Coerce(kxy,lis))[1][1];
      fi;
return rec(x:=r1,y:=Coerce(CoefficientRing(kx),r2),parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
fi;
end;







Subgroup:= function(P)
local i,L,stop,R;
L:= [];
stop := FALSE;
i := 1;
while stop = FALSE do
   R := _PointMult(i,P);
   L[i] := R;
   if (R.x = "INFTY POINT") then
      stop := TRUE;
   fi;
   i := i+1;
od;
if GetVerbose("Elliptic") <> 1 then
Print("Length: ",Length(L),"\n");
fi;
return L;
end;

_IndPoints:= function(P1,P2)
local L1,L2,i,j;
if P1 = P2 then return FALSE; fi;
L1 := Subgroup(P1);
L2 := Subgroup(P2);
for i in [1..Length(L2)] do
    if L2[i] in L1 then return FALSE;fi;
od;
return TRUE;
end;



_DisSub := function(P1,P2)
local sub1,sub2,i,j;
sub1:= Subgroup(P1);
sub2:= Subgroup(P2);
for i in [1..Length(sub1)-1] do
    for j in [1..Length(sub2)-1] do
        if sub1[i]= sub2[j] then return FALSE;
	fi;
    od;
od;
return TRUE;
end;





_Dep:= function(P1,P2)
local sub,i;
sub:= Subgroup(P2);
for i in [1..Length(sub)] do
    if sub[i] = P1 then return TRUE;
    fi;
od;
return FALSE;
end;




__Order2 := function(P)
local q,Q,L,par,m,j,k,boo1,boo2,R,l,neg,M,fact,newF,i,found,tried,testP,n,ok;
if P.x = "INFTY POINT" then
     return 1;
fi;
par:= P.parent;
q := Size(par.F);
Q := _PointMult(q+1,P);
m:= (Ceiling(q^(1/4)))+1;
if m > 2^27 then return Error("Curve too large, cannot compute order of point."); fi;
L := [];
L[1]:= rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
for j in [1..2*m-1] do
    L[j+1] := L[j] + P;
od;
found := FALSE;
tried := FALSE;
while (found = FALSE) do
   if tried = FALSE then
      k := -1;
   fi;
   boo1 := TRUE;
   boo2 := TRUE;
   neg := FALSE;
   while (boo1) do
     k := k+1;
     R := Q + _PointMult(k*2*m,P);
     l := 0;
     while (boo2 and l<Length(L)) do
       l := l+1;
          if L[l] = R then
             boo2 := FALSE;
	     boo1 := FALSE;
          fi;
     od;
   od;
   if (boo1= TRUE and boo2 = TRUE) then Print("noMatch found\n");fi;
   j:= l-1;
   M := q+1+2*m*k-j;
   newF := TRUE;
   while (newF = TRUE) do
     newF := FALSE;
     if M = 0 then
        found := FALSE;
	tried := TRUE;
     else  fact := Factorization(M);
         testP := (_PointMult(M,P));
         if testP.x <>"INFTY POINT" then Print("Error!,_Order\n");fi;
         found := TRUE;
	 i := 0;
	 ok := TRUE;
         while ok= TRUE and i <Length(fact) do
	     i := i+1;
             if _PointMult(Floor(M/((fact[i][1]))),P) = rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops) then
             M := Abs(Floor(M/((fact[i][1]))));
	     newF := TRUE;
	     ok := FALSE;
         fi;
         od;
     fi;
     od;
od;
return Abs(M);
end;

__Ordernor:= function(P)
local P2,i,j,L1,L2,p,s,Q,R,val,zer,E,found,m;
E:= Parent(P);
p:= Size(BaseRing(E));
s:= Ceiling(p^(1/4));
L1:=[P];
zer:= Zero(E);
if P = zer then
   return 1;
fi;
for i in [2..s] do
    L1[i]:= L1[i-1] +P;
    if L1[i] = zer then return i;
    fi;
od;
Q:= (2*s+1)*P;
R:= (p+1)*P;
found:=false;
j:=0;
while not found do
      if j > 0 then
          R:= R+Q;
      fi;
      for i in [1..Length(L1)] do
          if R = L1[i] then
             found:=true;
             val :=i;
             i:= Length(L1);
          elif R = -L1[i] then  
             found:=true;
             val := -i;
             i:= Length(L1);
          fi;
     od;
     j:=j+1;
od;
m:= p+1+(2*s+1)*(j-1)-val;
for i in [s..m] do
   if m mod i = 0 then
      if i*P = zer then
          #Print(P,i,"\n");
          return i;
      fi;
   fi;
od;
#Print(P,m,"\n");
return m;
end;  



__Ordernor2 := function(P)
local q,Q,L,par,m,j,k,boo1,boo2,R,l,neg,M,fact,newF,i,found,tried,testP,n,ok;
if P.x = "INFTY POINT" then
     return 1;
fi;
par:= P.parent;
q := Size(par.F);
Q := _PointMult(q+1,P);
m:= (Ceiling(q^(1/4)))+1;
if m >=2^28 then return Error("Curve too large, cannot compute order of point.\n"); fi; 
L := [];
L[1]:= rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops);
for j in [1..m-1] do
    L[j+1] := L[j] + P;
od;
if Characteristic(par.F) =2 then
   for j in [m..2*m-1] do
    L[j+1] := L[j] + P;
   od;
fi;
found := FALSE;
tried := FALSE;
while (found = FALSE) do
   if tried = FALSE then
      k := -m-1;
   fi;
   boo1 := TRUE;
   boo2 := TRUE;
   neg := FALSE;
   while (boo1 ) do
     k := k+1;
     R := Q + _PointMult(k*2*m,P);
     l := 0;
     while (boo2 and l<Length(L)) do
       l := l+1;
          if R =  _PointMult(-1,L[l]) then
             boo2 := FALSE;
	     boo1 := FALSE;
	     neg := TRUE;
          elif L[l] = R then
             boo2 := FALSE;
	     boo1 := FALSE;
          fi;

     od;
   od;
   if (boo1= TRUE and boo2 = TRUE) then Print("noMatch found\n");fi;
   j:= l-1;
   if (Characteristic(par.F) =2) then
      M := q+1+Abs(2*m*k);
   else M:= q+1+2*m*k;
   fi;
   if (neg and (Characteristic(par.F) <>2))then  M := M+j;
   else M := M-j;
   fi;

   newF := TRUE;
   while (newF = TRUE) do
     newF := FALSE;
     if M = 0 then
        found := FALSE;
	tried := TRUE;
     else  fact := Factorization(M);
         testP := (_PointMult(M,P));
         if testP.x <>"INFTY POINT" then Print("Error!,_Order\n");fi;
         found := TRUE;
	 i := 0;
	 ok := TRUE;
         while ok= TRUE and i <Length(fact) do
	     i := i+1;
             if _PointMult(Floor(M/((fact[i][1]))),P) = rec(x := "INFTY POINT",parent:= P.parent,type := elt-grp^ell,operations := _elt_grp_ell_ops) then
             M := Abs(Floor(M/((fact[i][1]))));
	     newF := TRUE;
	     ok := FALSE;
         fi;
         od;
     fi;
     od;
od;
return Abs(M);
end;

_Order:= function(P)
local par;
par := P.parent;
if Characteristic(par.F) =2 then return __Order2(P);
else return __Ordernor(P);
fi;
end;

_Size2:=function(EC)
local found,P,N,q,o,i,k,l,intFound,ok,Poi,j,m,op,oq,fo;
if EC.size<> -1 then return EC.size; fi;
SetVerbose("Elliptic",1);
found := FALSE;
N:= 1;
q := Size(EC.F);
if q<11 then return Length(Points(EC)); fi;
Poi:= [];
j := 0;
fo :=0;
while found = FALSE do
   P := RandomPoint(EC);
   o := _Order(P);
   k := LCM(o,N);
   if k <> N then
        Poi[Length(Poi)+1]:= P;
   elif k=N then
      if q<37 and fo >4 then
         return Length(Points(EC)); #verhindert Endlosschleife fuer den Fall dass 2*untere Schranke <=obere Schranke, tritt nur fuer q < 37 auf
      fi;
      for i in [1..Length(Poi)] do
          if(_Order(Poi[i])mod o = 0 or o mod _Order(Poi[i]) = 0 ) then
	     if _IndPoints(P,Poi[i]) then
	        k := k*o;
		Append(Poi,[P]);
	     fi;
	  fi;
      od;
   fi;
   i:=0;
   intFound := FALSE;
   ok := TRUE;
   while ok do
        i:= i+1;
        l := i*k;
	if ((l >= q+1 - Floor(2*(Sqrt(q)))) and (l <= q+1 + 2*(Ceiling(Sqrt(q))))) then
	    if intFound = FALSE then intFound := TRUE; fo:=fo+1;
	    else ok := FALSE;N:= LCM(o,N);
	    fi;
        elif (l > q+1 + 2*(Ceiling(Sqrt(q)))) then
            if intFound = TRUE then found := TRUE;N:= l-k;
	    fi;
	    ok := FALSE;
	    N:= LCM(o,N);
	fi;
   od;
   j := j+1;
od;
EC.size := N;
return N;
end;


_SizeSmall:= function(E)
   local sum,p,i,L,a,b;
p:= Size(BaseRing(E));
L:= E.L;
if Length(L) <> 2 then return Error("missing case");
fi;
a:= Coerce(Integers(),L[1]);
b:= Coerce(Integers(),L[2]);
sum:=0;
for i in [0..p-1] do
    sum := sum+ LegendreSymbol(i^3+a*i+b, p);
od;
return 1+p+sum;
end; 

_SizeTest:= function(k)
local i,j,exi,si,E;
for i in [2..k] do
   for j in [1..k] do
       exi:= ExistsEllipticCurve(k,[i,j]);
       if exi then
          E:= exi.ext1;
          si:= Size(E);
          Print("i: ",i," j: ",j," Size: ",si,"\n");
          if Length(Points(E)) <> si then
             return Error("wrong calculation!");
          fi;
       fi;
    od;
od;
end;

QuadraticTwist:= function(E,n)
local BR,L,a,b;
BR:=BaseRing(E);
if AllRoots(Coerce(BR,n),2) <> Sequence([]) then
   return Error("n is a square in base ring of E");
fi;
L:= E.L;
if Length(L) <> 2 then 
   return Error ("missing case");
fi;
a:= L[1];
b:= L[2];
return EllipticCurve(BR,[(n^2)*a,(n^3)*b]);
end; 

_Size3:= function(EC)
local sum,i,p,EC2,n,found,P,N2,P2,N,q,o,o2,k2,intFound2,l2,ok2,i,k,l,intFound,ok,Poi1,j,m,op,oq,fo,ords1,time,upBo,loBo,Poi2,ords2,BR,g;
#time:= Realtime();
if EC.size<> -1 then return EC.size; fi;
SetVerbose("Elliptic",1);
BR:= BaseRing(EC);
p:= Size(BR);
if IsPrime(p) then
  if p <230 then
    return _SizeSmall(EC);
  fi;
fi;
  #else
    n:=1;
    if IsPrime(p) then
      while not LegendreSymbol(n,p) = -1 do
          n:= n+1;
      od; 
    else n:= BR.1;
       g:=n;
      while not AllRoots(n,2) = Sequence([]) and n <> 1 do
       n:= g*n;
      od;
      if n = 1 then return Error;
      fi;
    fi;
    EC2:= QuadraticTwist(EC,n);
    found := 0;
N:= 1;
N2:=1;
q := Size(EC.F);
Poi1:= [];
ords1:=[];
Poi2:=[];
ords2:=[];
j := 0;
upBo:=q+1 + Ceiling(2*(Sqrt(q)));
loBo:= q+1 - Floor(2*(Sqrt(q)));
while found = 0 do
   #Print(Length(Poi),"\n");
   P := RandomPoint(EC);
   P2:= RandomPoint(EC2);
   while P in Poi1 do
      P := RandomPoint(EC);
   od;
   while P2 in Poi2 do
      P2 := RandomPoint(EC2);
   od;
   o := _Order(P);
   k := LCM(o,N);
   o2 := _Order(P2);
   k2 := LCM(o2,N2);
   #Print("lcm, k: ",k,"\n");
   if k <> N then
        Poi1[Length(Poi1)+1]:= P;
        #Print(P,"\n");
        ords1:=Append(ords1,[o]);
   elif k=N then
      i:=0;
      while i <Length(Poi1) do
          i:=i+1;
          if(ords1[i] mod o = 0 or o mod ords1[i] = 0 ) then
	        if not _IndPoints(P,Poi1[i]) then
                   i:= Length(Poi1)+2;
                fi;
	  fi;
      od;
      if i = Length(Poi1) then 
         k := k*o;
                #Print("indpoi, k: ",k,"\n");
                #Print(P,"\n");
		Poi1:=Append(Poi1,[P]);
                ords1:= Append(ords1,[o]);
      fi; 
   fi;
   if k2 <> N2 then
        Poi2[Length(Poi2)+1]:= P2;
        #Print(P,"\n");
        ords2:=Append(ords2,[o2]);
   elif k2=N2 then
      i:=0;
      while i <Length(Poi2) do
          i:=i+1;
          if(ords2[i] mod o2 = 0 or o2 mod ords2[i] = 0 ) then
	        if not _IndPoints(P2,Poi2[i]) then
                   i:= Length(Poi2)+2;
                fi;
	  fi;
      od;
      if i = Length(Poi2) then 
         k2 := k2*o2;
                #Print("indpoi, k: ",k,"\n");
                #Print(P,"\n");
		Poi2:=Append(Poi2,[P2]);
                ords2:= Append(ords2,[o2]);
      fi; 
   fi;
   i:=0;
   intFound := FALSE;
   intFound2:= FALSE;
   ok := TRUE;
   ok2:=true;
   while ok and ok2 do
        i:= i+1;
        l := i*k;
        l2:= i*k2;
	if ((l >= loBo) and (l <= upBo)) then
	    if intFound = FALSE then intFound := TRUE; 
	    else ok := FALSE;N:= LCM(o,N);
	    fi;
        elif (l > upBo) then
               if  intFound = TRUE then found := 1;N:= l-k;
	       fi;
	       ok := FALSE;
	       N:= LCM(o,N);
	fi;
        if ((l2 >= loBo) and (l2 <= upBo)) then
	    if intFound2 = FALSE then intFound2 := TRUE;
	    else ok2 := FALSE;N2:= LCM(o2,N2);
	    fi;
        elif (l2 > upBo) then
               if  intFound2 = TRUE then found := 2;N2:= l2-k2;
	       fi;
	       ok2 := FALSE;
	       N2:= LCM(o2,N2);
	fi;
   od;
   j := j+1;
od;
if found = 1 then
  EC.size := N;
  #Print(Poi1,"\n");
  #Print(Poi2,"\n");
  #Print("Time: ",Realtime()-time);
  return N;
elif found = 2 then
  EC.size:= 2*(p+1)-N2;
  #Print(Poi1,"\n");
  #Print(Poi2,"\n");
  #Print("Time: ",Realtime()-time);
  return EC.size;
fi;
#fi;
#else return Error("missing case");
#fi;
end;


_Size:=function(EC)
local found,P,N,q,o,i,k,l,intFound,ok,Poi,j,m,op,oq,fo,ords,time,upBo,loBo;
time:=Realtime();
if EC.size<> -1 then return EC.size; fi;
SetVerbose("Elliptic",1);
found := FALSE;
N:= 1;
q := Size(EC.F);
if q<11 then return Length(Points(EC)); fi;
Poi:= [];
ords:=[];
j := 0;
fo :=0;
upBo:=q+1 + Ceiling(2*(Sqrt(q)));
loBo:= q+1 - Floor(2*(Sqrt(q)));
while found = FALSE do
   #Print(Length(Poi),"\n");
   P := RandomPoint(EC);
   while P in Poi do
      P := RandomPoint(EC);
   od;
   o := _Order(P);
   k := LCM(o,N);
   #Print("lcm, k: ",k,"\n");
   if k <> N then
        Poi[Length(Poi)+1]:= P;
        Print(P,"\n");
        ords:=Append(ords,[o]);
   elif k=N then
      if q<37 and fo >4 then
         return Length(Points(EC)); #verhindert Endlosschleife fuer den Fall dass 2*untere Schranke <=obere Schranke, tritt nur fuer q < 37 auf
      fi;
      i:=0;
      while i <Length(Poi) do
          i:=i+1;
          if(ords[i] mod o = 0 or o mod ords[i] = 0 ) then
	        if not _IndPoints(P,Poi[i]) then
                   i:= Length(Poi)+2;
                fi;
	  fi;
      od;
      if i = Length(Poi) then 
         k := k*o;
                #Print("indpoi, k: ",k,"\n");
                Print(P,"\n");
		Poi:=Append(Poi,[P]);
                ords:= Append(ords,[o]);
      fi; 
   fi;
   i:=0;
   intFound := FALSE;
   ok := TRUE;
   while ok do
        i:= i+1;
        l := i*k;
	if ((l >= loBo) and (l <= upBo)) then
	    if intFound = FALSE then intFound := TRUE; fo:=fo+1;
	    else ok := FALSE;N:= LCM(o,N);
	    fi;
        elif (l > upBo) then
            if intFound = TRUE then found := TRUE;N:= l-k;
	    fi;
	    ok := FALSE;
	    N:= LCM(o,N);
	fi;
   od;
   j := j+1;
od;
EC.size := N;
Print("time: ",Realtime()-time,"\n");
return N;
end;

#find k with k*A = B
DiscreteLog:= function(A,B)
local N,m,L,i,j,R,found;

if A.parent <>B.parent then
   Print("Points not defined over the same curve.\n");
   return;
fi;
N := _Size(A.parent);
m := Ceiling(Sqrt(N));
L := [];
for i in [1..m+1] do
    L[i] := _PointMult(i-1,A);
od;
for j in [1..m+1] do
    R :=  B - _PointMult((j-1)*m,A);
    for i in [1..m+1] do
        if R = L[i] then
	   return ((i-1) + (j-1)*m) mod _Order(A);
	fi;
    od;
od;
Print("There is no k with k*",A,"=",B,".\n");
end;





_ECSubEq:= function(L1,L2)
local i,j,found;
if Length(L1)<>Length(L2) then
return FALSE;
fi;
for i in [1..Length(L1)] do
    found := FALSE;
    j := 0;
    while (found =FALSE and j<Length(L2)) do
          j:= j+1;
	  if L2[j]= L1[i] then
	     found := TRUE;
	  fi;
    od;
    if found = FALSE then
       Print(L1[i],"\n");
    return FALSE;
    fi;
od;
return TRUE;
end;


_DisSub2:= function(L1,L2)
local i,j;
for i in [1..Length(L1)-1] do
    for j in [1..Length(L2)-1] do
        if L1[i] = L2[j] then return FALSE;
        fi;
    od;
od;
return TRUE;
end;

_Generator:= function(EC)
local N,Points,P,o1,o,sub,R,op;
N:= _Size(EC);
P:= RandomPoint(EC);
Points:= [P];
o1:= _Order(P);
if o1=N then return Points;
else sub:= Subgroup(Points[1]);
     while o1<N do
           P:= RandomPoint(EC);
	   op:= _Order(P);
	   if op = N/o1 then
	      if _DisSub2(sub,Subgroup(P)) then
	         Points[2] := P;
		 o1 := N;
	      fi;
	   elif op = N then Points[1]:= P; return Points;
	   elif op>o1 then Points[1]:= P; o1:=op;sub:= Subgroup(P);
	  # else P:=Points[1]+P; op:= _Order(P);
	   #     if op>o1 then Points[1]:= P; o1:=op;sub:= Subgroup(P);fi;
	   fi;
     od;
     if Length(Points)=1 then return Points;
     else
         R:=Points[1]+Points[2];
         if _Order(R) = N then
            return [R];
          else return Points;
         fi;
     fi;
fi;
end;


_HasSingularPoints := function(F,L)
local x,y,f,f1,f2,g1,g2,L1,L2,i,j,Li,fx,fy,kx;
if Coerce(F,_Discriminant(F,L))<>0 then
   return FALSE;
elif Characteristic(F) <>2 then
   f:= _GetPoly(F,L);
   f1:= Evaluate(f,0);
   Li:= Roots(f1);
   if Length(Li)=3 then
      return FALSE;
   else
   for i in [1..Length(Li)] do
       if Li[i][2] >1
          then  Print("Singular point found: [",Li[i][1],",0]\n");
       return TRUE;
       fi;
   od;
   fi;
   return Error("disc =0 but found no singular point\n");
else
    kx:= PolynomialAlgebra(F);
    f:= _GetPoly(F,L);
    fy:= Derivative(f);
    fx:= Derivative(_SwapVars(f));
    L:= ElementToSequence(fy);
    fy:= Coerce(kx,L[1]);
    L1:= Roots(fy);
    fx:= Evaluate(fx,L1[1][1]);
    L2:= Roots(fx);
    y:= L2[1][1];
    x:= L1[1][1];
    if(Evaluate(Evaluate(f,y),x)) = 0 then
      Print("Singular point found: [",x,",",y,"]\n");
      return TRUE;
    fi;

fi;
return;
end;

_Divisor :=function(P)
local Pl;
Pl:=_Place(P);
return Divisor(Pl);
end;

_GroundPlace:= function(P)
local base;
base:= Parent(P);
while not Is(Type(base),fld^fin) do
      base:= BaseRing(base);
od;
return base;
end; 

MakePoint:= function(EC,L)
local f,p,x,y,P,d,Pl;
if L=INFTY or L[1] = INFTY then
   return rec(x := "INFTY POINT",parent:= EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
fi;
x:=Coerce(EC.F,L[1]);
y:=Coerce(EC.F,L[2]);
p:= _GetPoly(EC.F,EC.L);
f:= Evaluate(p,y);
if(Evaluate(f,x) <>0 ) then
   Print(L,"Given coordinates do not define a point on given curve\n");
   return;
fi;
P:=rec(x:=x,y:=y,parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
if Is(Type(EC.F),fld^fin) then
 Pl:= _Place(P);
d:=Degree(Pl);
if (d <>1) then
    Print("Given coordinates do not define a point on given curve\n");
fi;
fi;
return P;
end;


_CoercePoint:= function(E,P)
if IsList(P) then return MakePoint(E,P);fi;
return MakePoint(E,[X(P),Y(P)]);
end;


MakePoints:= function(EC,x)
local p,f,R,P,Pl,d,i;
if x=INFTY then
    return rec(x := "INFTY POINT",parent:= EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
fi;
x:= Coerce(EC.F,x);
p:= _SwapVars(_GetPoly(EC.F,EC.L));
f:= Evaluate(p,x);
R:= Roots(f);
P:= [];
for i in [1..Length(R)] do
    P[i]:=rec(x:=x,y:=R[i][1],parent := EC,type := elt-grp^ell,operations := _elt_grp_ell_ops);
    Pl:= _Place(P[i]);
    d:=Degree(Pl);
    if (d <>1) then
        Remove_(P,i);
    fi;
od;
return P;
end;

_BaseRing:= function(EC) 
return EC.F;
end;

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "EllipticCurve",
sin := [[elt-ord^rat,"q"],[list,"L"]],
sou := [[grp^ell]],
short :=
"Constructs an elliptic curve over the finite field with q elements. If L has length 2, the curve is "+
"defined by the equation y^2 = x^3 + L[1]*x + L[2], if L has length 5, the curve is  "+
"defined by the equation y^2 + L[1]*x*y + L[2]*y = x^3 + L[3]*x^2 + L[4]*x + L[5]. ",
ex:=["EC:= EllipticCurve(5,[1,2]);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "Points",
sin := [[grp^ell,"E"]],
sou := [[list,"L"]],
short :="Returns all Points of E as a list.",
ex:=["EC:= EllipticCurve(5,[1,2]);;\nPoints(EC);\n"]
));


InstallDocumentation(
rec(
kind := "FUNCTION",
name := "MakePoint",
sin := [[grp^ell,"EC"],[list,"L"]],
sou := [[elt-grp^ell,"P"]],
short :="Returns,if possible, a point on EC, defined by the coordinates given in L.",
ex:=["EC:= EllipticCurve(5,[1,2]);;\nMakePoint(EC,[4,0]);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "MakePoints",
sin := [[grp^ell,"EC"],[elt-fld^fin,"x"]],
sou := [[elt-grp^ell,"P"]],
short :="Returns the points on EC with x-coordinate x.",
ex:=["EC:= EllipticCurve(5,[1,2]);;\nMakePoints(EC,1);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "RandomPoint",
sin := [[grp^ell,"EC"]],
sou := [[elt-grp^ell,"P"]],
short :="Returns a random point of EC, but never the point at infinity.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nRandomPoint(EC);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "RandomPointWithInf",
sin := [[grp^ell,"EC"]],
sou := [[elt-grp^ell,"P"]],
short :="Returns a random point of EC.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nRandomPointWithInf(EC);\n"]
));


InstallDocumentation(
rec(
kind := "FUNCTION",
name := "Point",
sin := [[elt-pls/fld^fun,"p"],[elt-grp^ell,"EC"]],
sou := [[elt-grp^ell,"P"]],
short :="Returns the point on EC corresponding to p.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nRandomPoint(EC);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "EllipticFunctionField",
sin := [[grp^ell,"EC"]],
sou := [[fld^fun,"F"]],
short :="Returns the function field of genus one corresponding to EC.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nRandomPoint(EC);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "ShortWeierstrassNormalForm",
sin := [[grp^ell,"EC"]],
sou := [[grp^ell,"EC2"]],
short :="Returns the isomorphic curve EC2 defined by a polynomial of the form y^2=x^3+a*x+b.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nRandomPoint(EC);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "jInvariant",
sin := [[elt-grp^ell,"EC"]],
sou := [[elt-fld^fin,"j"]],
short :="Returns the j-invariant of EC.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\njInvariant(EC);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "Subgroup",
sin := [[elt-grp^ell,"EC"]],
sou := [[list,"L"]],
short :="Returns the subgroup generated by EC.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);;\n"+"Subgroup(P);\n"]
));

InstallDocumentation(
rec(
kind := "FUNCTION",
name := "DiscreteLog",
sin := [[elt-grp^ell,"P"],[elt-grp^ell,"Q"]],
sou := [[elt-ord^rat,"n"]],
short :="Returns, if possible, n satisfying n*P = Q.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nP:= Generator(EC)[1];;\n"+"Q:= RandomPoint(EC);;\nDiscreteLog(P,Q);\n"]
));

InstallMethod(
rec(
kind := "FUNCTION",
name := "Discriminant",
sin := [[grp^ell,"EC"]],
sou := [[elt-fld^fin,"d"]],
short :="The discriminant of EC.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nDiscriminant(EC);\n"]
),_Discriminant2);


InstallMethod(
rec(
kind := "FUNCTION",
name := "Divisor",
sin := [[elt-grp^ell,"P"]],
sou := [[elt-dvs/fld^fun,"D"]],
short :="The divisor corresponding to P.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);;\nDivisor(P);"]
),_Divisor);

InstallMethod(
rec(
kind := "FUNCTION",
name := "BaseRing",
sin := [[grp^ell,"EC"]],
sou := [[fld^fin,"F"]],
short :="The field over which EC is defined.",
ex:= ["EC:= EllipticCurve(5,[1,2]);;\nF:= BaseRing(EC);;\n"]
),_BaseRing);


InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Order",
    sin:=[[elt-grp^ell,"P"]],
    sou:=[[elt-ord^rat,"n"]],
    short:=
      "The order of the point P, only works for \"small\" curves. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);;\n"+"Order(P);\n"
        ],
    see:=[]), _Order);


InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Generator",
    sin:=[[grp^ell,"EC"]],
    sou:=[[list,"L"]],
    short:=
      "Returns a list of generator(s) of EC, only works for \"small\" curves. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nL:= Generator(EC);\n"
        ],
    see:=[]), _Generator);

InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Place",
    sin:=[[elt-grp^ell,"P"]],
    sou:=[[elt-pls/fld^fun,"p"]],
    short:=
      "The place of degree one corresponding to P ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);;\n"+"Place(P);\n"
        ],
    see:=[]), _Place);

InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Size",
    sin:=[[grp^ell,"P"]],
    sou:=[[elt-ord^rat,"n"]],
    short:=
      "The number of points belonging to EC, only works for \"small\" curves. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nSize(EC);\n"
        ],
    see:=[]), _Size3);
InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Coerce",
    sin:=[[grp^ell,"EC"],[elt-grp^ell,"P"]],
    sou:=[[elt-grp^ell,"P2"]],
    short:=
      "Coerces P into EC. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);\n"+
         "Coerce(EllipticCurve(5^2,[1,2]),P);" ],
    see:=[]), _CoercePoint);

InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Coerce",
    sin:=[[grp^ell,"EC"],[list,"L"]],
    sou:=[[elt-grp^ell,"P"]],
    short:=
      "Coerces L into EC. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);\n"+
         "Coerce(EllipticCurve(5^2,[1,2]),P);" ],
    see:=[]), _CoercePoint);

InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Zero",
    sin:=[[grp^ell,"EC"]],
    sou:=[[elt-grp^ell,"P"]],
    short:=
      "Returns the point at infinity. ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= Zero(EC);\n"
        ],
    see:=[]), _Zero);

InstallMethod(
rec(
    kind:="FUNCTION",
    name:="Parent",
    sin:=[[elt-grp^ell,"P"]],
    sou:=[[grp^ell,"EC"]],
    short:=
      "The default parent of P ",
    ex:=["EC:= EllipticCurve(5,[1,2]);;\nP:= RandomPoint(EC);;\n"+"Parent(P)=EC;\n"
        ],
    see:=[]), _Parent);
