
#########################################################################
# these programs were written  by   Eric Von York    ####################
# authors copyright 1992   all rights reserved       ####################
#########################################################################

macro( FP = `elliptic/FP`, babyset = `elliptic/babyset`, 
      plus = `elliptic/plus`, nP = `elliptic/nP`, 
      powers_of_2 = `elliptic/powers_of_2`,
      giantset = `elliptic/giantset`, 
      gsEmodP = `elliptic/gsEmodP`);


`help/text/elliptic`  := TEXT(
`FUNCTION: elliptic -  determines the order of the group of points`, 
`on a non-singular elliptic  curve over a finite field of type Z/<p>  ` ,
`   `,
`  ` ,
`CALLING SEQUENCE:   elliptic(p, A, B);`,
`  ` , 
`PARAMETERS: p  -  the prime number that determines the field`,
`          A,B  -  the coefficients of the elliptic curve  y^2 = x^3 + Ax + B`,
`  `,
`SYNOPSIS:   `,
`   `,
` - The call elliptic(p,A,B) returns the order of the group of`,
`   points on the elliptic curve over a finite field Z/<p> where the group `,
`   operation used is derived from the chord tangent law of composition`,
`  `,
` -This procedure was designed to be used with fields having order smaller`, 
`  than 10^16, however, this may varie depending on the system used`, 
`  `,
` -The identity of the group is denoted by (ID,ID)`,
`  `,
`EXAMPLES:   `,
`  `,
`>elliptic(5,1,1); `,
`   `,
`                                               9`,
`>elliptic(47,1,1);`,
`   `,
`                                              60`,
`> p := nextprime(10^15);`,
`  `,
`                                        1000000000000037`,
`>elliptic(p,2345,734);`,
`  `,
`                                        999999945752832 `
):


###############################################################
#  NOTE:   In the following procedures  `p'  corresponds to the prime number 
#          that determines the field  Z/<p>,  and  `A '  
#          and `B' correspond to the coefficients of the elliptic curve  
#          y^2 = x^3 + A*x + B where 4*A^3 + 27*B^2  is           
#          not zero modulo p.
###############################################################                                                                                                                

#-------------------------------------------------------------------------#
#  INPUT: P and Q , where P and Q are points on an elliptic 
#         curve over a field Z/<p>.
# OUTPUT:   P+Q
# The points must be entered as a list containing two eletements. ie:[x,y].
# For example:    plus([x1,y1],[x2,y2]);  


plus := proc(P,Q, p,A)

     local m ,                 # The slope of the line joining the two points.
          x1,y1,               # The coordinates of P.
          x2, y2 ,             # The coordinates of Q.
	  x3, y3;              # The coordinates of  P+Q.

        x1 := op(1,P);  x2 := op(1,Q); y1 := op(2,P);  y2 := op(2,Q);
        if x1 = ID then x3 := x2;  y3 := y2;
        elif x2 = ID then x3 := x1;  y3 := y1;
        elif x1 <> x2 then
               m  := ((y2-y1)/(x2-x1)) mod p;
               x3 := (-x1 - x2 + m^2) mod p;
               y3 := (-y1 + m*(x1 - x3)) mod p;
        elif y1 = y2 and y1 <> 0 then
               m := ((3*x1^2 + A)/(2*y1)) mod p; 
               x3 := (-x1 - x2 + m^2) mod p;
               y3 := (-y1 + m*(x1 - x3)) mod p;
       else
          x3 := ID;  y3 := ID; fi;
     [x3,y3];
end:     

#______________________________________________________________________ 
#

#  INPUT:  s, P   where s is a positive integer and  P is a point 
#         on an elliptic curve over Z/<p>.
# OUTPUT:  s*P = P + P + P + P + P + P  +  ..........  + P  (s times) 


nP := proc(s,P,p,A)

      local Q1, bi_s, numdigits, pointlist, bistring, i;
    

       if s = 0 then
          Q1 := [ID,ID];
       else
          bi_s := convert(s,binary);
          bistring := d.bi_s;
          numdigits := length(bi_s);
          pointlist := powers_of_2(numdigits,P,p,A);
          Q1 := pointlist[1];
          for i from  3 to numdigits+1  do 
              if substring(`bistring`,i..i) = `1` then
                 Q1 := plus(Q1,pointlist[i-1],p,A); fi;
          od; fi;
        Q1;
end:
#_____________________________________________________________________#
# Input:  c, N   where c is a positive integer corresponding to the length of 
# a binary digit,  and N is a point on an elliptic curve over a field Z/<p>.
# OUTPUT:  [ (2^c)*N, (2^(c-1))*N,  ....    , 2*N,  N ] 


powers_of_2 := proc(c,N,p,A)
       local two_nthset, Ni, i;
       Ni := N;
       two_nthset := [N];
       for i to c-1 do
           Ni := plus(Ni,Ni,p,A);
           two_nthset := [Ni,op(two_nthset)];
       od;
    two_nthset;
end:
#_____________________________________________________________________#
# OUTPUT : P  where P is a random point on an elliptic curve over Z/<p>.

FP := proc(p,A,B)
     local X, w ,pnt ,Y,  Yvals, Xr, y;
     Xr := rand(1..p-1);
     w := -1;
     while w < 0 do
     X := Xr();
     w := numtheory['legendre'](X^3 + A*X + B,p);
     od;
     Yvals := {msolve(y^2 = X^3 + A*X + B,p)};
     Y := subs(op(1,op(1,Yvals)),y); 

     pnt := [X,Y];
     pnt;
end:
#_______________________________________________________ 
# INPUT: l_p - l_p is the size of the set to be computed and it is a 
#              function of the field order 
# OUTPUT: [ P, BS] - P is a point on the elliptic curve and BS is a 
#         list that contains the x-values of the following set  
#         {0*P, P, 2P, 3P, ...  l_p*P} .   The point order    
#         is ensured to be at least 2*l_p.

babyset := proc(l_p, p,A,B)

       local P1, Pnt, xlist, i;
       xlist := [ID];
       while nops(xlist) = 1  do
              Pnt := FP(p,A,B);
              P1 := Pnt;
        for i from 2 to l_p do       
                xlist := [op(xlist),op(1,P1)];
                P1 := plus(Pnt,P1,p,A);
                if member(op(1,P1), xlist)  then
		  Pnt := FP(p,A,B);  P1 := Pnt;  xlist := [ID]; i := l_p;
                fi;
         od;
       od;
       [Pnt,xlist];
end:
#___________________________________________________________________#
#  INPUT: l,low - two variables whose value depends on p.
#  P, x_babyset  - these values are the output from the procedure babyset.
#  OUTPUT:[a,b]  - a is either the order of the point P,  or the group order
#        - b is a boolean which is true if the gsEmodP procedure is needed
#          false if it is not needed.

giantset := proc(l, low,  P,  x_babyset,p,A)

       local  hit, hits,  hitlist, i, v1, Ht, Q,  xset, xlist, high;
       high := trunc(evalf(p+1+2*p^(.5)));
       xlist := x_babyset;
       hits := [];  xset := {op(xlist)};
       Q :=  nP(l,P,p,A);
       Ht := nP(low,P,p,A);
       if (member(op(1,Ht),xlist,v1) and not(Ht = nP(v1-1,P,p,A))) then
           hits := [low + v1-1]; fi;  
       v1 := 'v1';  
     for i to l-1 do
         Ht := plus(Ht,Q,p,A);
         if member(op(1,Ht), xset)  then
	    member(op(1,Ht), xlist, v1);  
	    if Ht = nP(v1-1,P,p,A)   then hit := low + i*l -  v1 + 1;
	    else hit :=  low + i*l + v1 - 1; fi;
	if not member(hit,hits)  then hits := [op(hits),hit]; fi;
         v1 := 'v1'; fi; 
      od; 
      Ht := plus(Ht,Q,p,A);
      if (member(op(1,Ht),xlist,v1) and Ht = nP(v1-1,P,p,A)) then
          hit :=   low + l^2 - v1 + 1; fi;
      if not member(hit,hits) and hit <= high then hits := [op(hits),hit]; fi;
      if nops(hits) > 1 then hits := [hits[1..2]]; fi;
      if nops(hits) = 1 then hitlist := [op(hits), false];
      else hitlist := [op(2,hits) - op(1,hits), true]; fi;
      hitlist;
end:
#____________________________________________________________________#
# INPUT: ordP - the order of the point P  where P is the point found in babyset.
#        P1  -  a second point on the elliptic curve.
#        Qg  -  l*P,   where l is defined in the main procedure.
#        x_s -  the list of x-values that were returned from babyset.
#        l   -  a variable that depends on p.
# OUTPUT:   EmodP -   the order of the Group   E(Zp)/<P>,  or 0 if 
#                     another attempt is needed.

gsEmodP := proc(ordP, P1, Qg, x_s, l,  p, A)

     local  flag, P2, P3, P4, ck_P,  z, in_P, EmodP, new_high, 
            new_low, new_l, i,  j, k; 


       new_low := trunc(evalf((p + 1 - 2*p^(.5))/ordP)) + 1;
       new_high := trunc(evalf((p + 1 + 2*p^(.5))/ordP));
       new_l := trunc(evalf((p+1+2*p^(.5))/((2*p^(.25))*ordP)))+1;
       EmodP := 0;  flag := 0;  in_P := false;
       P2 := nP(new_low,P1,p,A);
       z := new_high - new_low ;
       P3 := P1;
       ck_P := trunc(evalf(ordP/l)) + 1;
       for i from 0 to ck_P do
           if member(op(1,P3),x_s) then  in_P := true;
              i := ck_P;  fi;  P3 := plus(P3,Qg ,p,A);
       od;
       if not in_P then
          for j from 0 to z do P4 := P2;
              for k from 0 to new_l do
                  if member(op(1,P4),x_s) then
                     flag := flag + 1;   EmodP := new_low + j; k := new_l; fi;
                  P4 := plus(P4,Qg ,p,A);
              od;
              P2 := plus(P2,P1,p,A);
              if flag = 2 then
                  j := z; EmodP := 0; fi;
          od;
       fi;
      EmodP;
end:
#________________________________________________________________#
######                   MAIN PROCEDURE                     ######
#________________________________________________________________#
# INPUT: e - the prime that will determine the finite field.
#     f,g  - the coefficients of the elliptic curve y^2 = x^3 + ex + f.
# OUTPUT: Ek - the order of the group of points on y^2 = x^3 + ex + f  when
#              viewed over Z/<e>.   

 	
elliptic := proc(e,f,g)

  local    p, A, B, BS, Ek, Ek2, GNT_set, P, Q, low, l,
               l_prime, loop1, loop2, new_P;

    if ((4*f^3 + 27*g^2) mod e) = 0 then
     print(`Enter new values for A & B, the curve chosen is singular.`);
     Ek := 0;
   else
     p := e; A := f; B := g;
     l := trunc(evalf(2*p^(.25))) +1;
     l_prime := trunc(evalf(l/2)) +1;
     low := trunc(evalf(p+1-2*p^(.5))) ;
     loop1 := true;
     while  loop1 do
          BS := babyset(l_prime, p,A,B);
	  P := op(1,BS);
          Q :=  nP(l,P,p,A);
          GNT_set := giantset(l, low, P,op(2,BS),p,A);
	  Ek := op(1,GNT_set);
	  if Ek  >  evalf(p^.5) - 1 then loop1 := false; fi;
          loop2 := op(2,GNT_set);
     od; 
       while  loop2  do
           new_P := FP(p,A,B);
           Ek2 := gsEmodP(Ek, new_P, Q, op(2,BS), l,  p,A);
	   if Ek2 > 0 then loop2 := false; Ek := Ek2*Ek; fi ; 

     od; fi;
  Ek;
end:

macro( FP = FP, babyset = babyset, 
      plus = plus, nP = nP, 
      powers_of_2 = powers_of_2,giantset = giantset, 
      gsEmodP = gsEmodP);

#save `elliptic.m`;
#quit
