// Returns either 2*P or GCD(N,x1-x2) =/= 1
function double(P,a,N)  
   x,y,z := Explode(P);
   if z eq 0 then  // point at infinity
      return P;
   end if;
   g,_,y2inv := XGCD(N,Integers()!(2*y));
   if g ne 1 then
      return g;
   end if;
   xx := ((x^2-a)^2 - 8*x)*y2inv^2;
   yy := ((3*x^2 + a)*(x - xx) - 2*y^2)*y2inv;
   return [xx,yy,1];
end function;

// Returns P + Q or GCD(N,x1-x2) =/= 1
function add(P,Q,a,N)
   if P eq Q then
      return double(P,a,N);
   end if;
   x1,y1,z1 := Explode(P);
   x2,y2,z2 := Explode(Q);
   if z1 eq 0 then
      return Q;
   elif z2 eq 0 then
      return P;
   end if;
   if x1 eq x2 and y1 eq -y2 then
      return [0,1,0];
   end if;
   g,_,inv := XGCD(N,Integers()!(x1-x2));
   if g ne 1 then
      return g;
   end if;
   lambda := (y1-y2)*inv;
   nu := y1 - lambda*x1;
   x3 := lambda^2 -x1-x2;
   y3 :=  -lambda*x3-nu;
   return [x3,y3,1];
end function;

// Try to compute R=m*[0,1,1] on y^2=x^3+ax+1; returns
// either R or GCD(N,some denominator) =/= 1 if not possible.
function multiply(m,a,N)
   // Points are represented as triples [x,y,z] with z either 0 or 1.
   P := [IntegerRing(N)|0,1,1];   
   R := [IntegerRing(N)|0,1,0];
   while m ne 0 do
      if IsOdd(m) then
         R := add(R,P,a,N);
         if Type(R) eq RngIntElt then
            return R;
         end if;
      end if;         
      m := Floor(m/2);
      P := double(P,a,N);
      if Type(P) eq RngIntElt then
         return P;
      end if;
   end while;
   return R;
end function;

intrinsic ECM1(N::RngIntElt, m::RngIntElt, a::RngIntElt) -> RngIntElt
{Try to find a B-power smooth factor of N using Lenstra's ECM with given a
and m=lcm(1,...,B). Returns N on failure.}
   printf "Trying a = %o: \t", a;
   if GCD(4*a^3 + 27, N) ne 1 then
      print "Split using discriminant.";
      return GCD(4*a^3 + 27, N);
   end if;
   R := multiply(m,a,N);
   if Type(R) eq RngIntElt then
      printf "Failed to compute mP. ";
      if R lt N then
         print "Split using denominator.";
         return R;
      end if;
         print "Denominator gives no factor.";
   end if;
   print "Computed mP (no factor found).";
   return N;
end intrinsic;


intrinsic ECM(N::RngIntElt, B::RngIntElt, maxtries::RngIntElt) -> RngIntElt
{Try to find a B-power smooth factor of N using Lenstra's ECM. 
Returns N on failure.}
   m := LCM([1..B]);
   for i in [1..maxtries] do
      a := Random(N);
      M := ECM1(N,m,a);
      if M ne N then
         return M;
      end if;
   end for;
   print "Max tries exceeded.  Trying changing B.";   
   return N;
end intrinsic;
