/*********************************************************************12(c) Copyright 2006-2010 Salman Baig and Chris Hall34This file is part of ELLFF56ELLFF is free software: you can redistribute it and/or modify7it under the terms of the GNU General Public License as published by8the Free Software Foundation, either version 3 of the License, or9(at your option) any later version.1011ELLFF is distributed in the hope that it will be useful,12but WITHOUT ANY WARRANTY; without even the implied warranty of13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14GNU General Public License for more details.1516You should have received a copy of the GNU General Public License17along with this program. If not, see <http://www.gnu.org/licenses/>.1819*********************************************************************/2021//22// C++ elliptic curve class23//24// this code offers basic routines for elliptic curves over a finite field.25// currently it is limited to curves with a model of the form26// y^2=x^3+a4*x+a6, which excludes characteristic two and some curves in27// characteristic three. because the latter two characteristics present28// other difficulties for the L-function code, we felt it was ok to make29// this exclusion.30//31// our class is modeled after NTL classes such as the finite field class32// ZZ_pE. in particular, there is a global elliptic curve which is assumed33// to be defined over NTL's global finite field, and one must use context34// loading and unloading in order to switch between curves and finite35// fields. on the one hand, the SAGE people assure us that this has proven36// to be a source of many bugs because one must be very careful about37// switching to the appropriate context. on the other hand, we do not want38// to rewrite our code using a library other than NTL, so we will have to39// deal with this issue regardless of whether or not our own classes use40// the model.41//42// this code is *not* meant to provide full elliptic curve functionality,43// rather merely what we need to perform point counting. this means that44// we have code for computing P+Q and [m]P for points P,Q and an integer m,45// but these routines are only mildly optimized. our experience is that46// for a fixed finite field, one really only needs to use this code to47// compute the Euler factors for a (semi-)universal family over the j-line,48// and then to regard any other family as a combined pullback and quadratic49// twist. the point is that the Euler factors for the latter kind of curve50// can be computed without any elliptic curve arithmetic given the Euler51// factors for the former, and in particular, one only needs the elliptic52// curve code when getting started over a new finite field.53//5455#include <assert.h>56#include <string.h>57#include <NTL/lzz_p.h>58#include <NTL/lzz_pE.h>59#include <NTL/lzz_pX.h>60#include <NTL/lzz_pXFactoring.h>61#include <NTL/lzz_pX.h>6263NTL_CLIENT6465#include "ell.h"66#include "helper.h"67#include "lzz_pEExtra.h"6869#define INV_TABLE 1 // precompute a table of inverses in F_q^*?70#define X_ORDER 1 // order points by x-coordinates first? (then y?)7172///////////////////////////////////////////////////////////////////////////73// elliptic curve parameter class74// - we keep track of the coeffs a4,a6, the order of the finite field,75// and a table of inverses for F_q^*76///////////////////////////////////////////////////////////////////////////7778// constructor7980ell_pEInfoT::ell_pEInfoT(const zz_pE& new_a4, const zz_pE& new_a6)81{82ref_count = 1;8384a4 = new_a4;85a6 = new_a6;86q = to_long(zz_pE::cardinality());87}8889// destructor9091ell_pEInfoT::~ell_pEInfoT()92{93}9495// copied from NTL9697static void CopyPointer(ell_pEInfoPtr& dst, ell_pEInfoPtr src)98{99if (src == dst) return;100101if (dst) {102dst->ref_count--;103104if (dst->ref_count < 0)105Error("internal error: negative ell_pEContext ref_count");106107if (dst->ref_count == 0) delete dst;108}109110if (src) {111if (src->ref_count == NTL_MAX_LONG)112Error("internal error: ell_pEContext ref_count overflow");113114src->ref_count++;115}116117dst = src;118}119120////////////////////////////////////////////////////////////121// context switching class, shamelessly copied from NTL122////////////////////////////////////////////////////////////123124ell_pEInfoPtr ell_pEInfo = NULL; // info for current elliptic curve125126ell_pEContext::ell_pEContext(const zz_pE& a4, const zz_pE& a6)127{128ptr = new ell_pEInfoT(a4, a6);129}130131ell_pEContext::ell_pEContext(const ell_pEContext& a)132{133ptr = NULL;134CopyPointer(ptr, a.ptr);135}136137ell_pEContext& ell_pEContext::operator=(const ell_pEContext& a)138{139CopyPointer(ptr, a.ptr);140return *this;141}142143ell_pEContext::~ell_pEContext()144{145CopyPointer(ptr, NULL);146}147148void ell_pEContext::save()149{150CopyPointer(ptr, ell_pEInfo);151}152153void ell_pEContext::restore() const154{155CopyPointer(ell_pEInfo, ptr);156}157158//////////////////////////////159160void ell_pE::init(zz_pE& a4, zz_pE& a6)161{162ell_pEContext c(a4, a6);163c.restore();164}165166//////////////////////////////167168ellpoint_pE::ellpoint_pE()169{170x = 0;171y = 0;172identity_f = 1;173}174175ellpoint_pE::ellpoint_pE(const zz_pE& new_x, const zz_pE& new_y)176{177x = new_x;178y = new_y;179identity_f = 0;180}181182// P + Q = R183184void add(ellpoint_pE& R, const ellpoint_pE& P, const ellpoint_pE& Q)185{186// temporary variables for calculation187static zz_pE *Rx = NULL, *Ry = NULL, *Rz = NULL, *Ru = NULL;188static zz_pE *lambda = NULL;189190static long q = -1;191192// reuse point from previous invocation if possible193if (ell_pEInfo->q != q) {194q = ell_pEInfo->q;195if (lambda != NULL) {196delete lambda;197delete Rx;198delete Ry;199delete Rz;200delete Ru;201}202// NB: the only way the following are deallocated is if they203// are replaced during a later invocation (see previous204// lines), so technically this is a tiny memory leak205lambda = new zz_pE;206Rx = new zz_pE;207Ry = new zz_pE;208Rz = new zz_pE;209Ru = new zz_pE;210}211212// check if either is identity213if (P.identity_f) { R = Q; return; }214if (Q.identity_f) { R = P; return; }215216// calculate slope between lines217// - special cases arise when P=+/-Q218if (P.x == Q.x) {219if (P.y != Q.y || P.y == 0) {220R.set();221return;222}223224// point-doubling225if (zz_pEExtraInfo->inv_table != NULL) {226// lambda = (3*x(P)^2 + a4)/(2*y(P))227sqr(*Rx, P.x);228add(*Ry, *Rx, *Rx);229add(*Rz, *Rx, *Ry);230add(*Rx, *Rz, ell_pEInfo->a4);231add(*lambda, P.y, P.y);232*Ru = zz_pEExtraInfo->inv_table->table[to_ulong(*lambda)];233mul(*lambda, *Rx, *Ru);234} else {235*lambda = (3*sqr(P.x) + ell_pEInfo->a4) / (2*P.y);236}237} else {238if (zz_pEExtraInfo->inv_table != NULL) {239// lambda = (y(P)-y(Q))/(x(P)-x(Q))240sub(*Rz, P.x, Q.x);241*Ru = zz_pEExtraInfo->inv_table->table[to_ulong(*Rz)];242sub(*Rz, P.y, Q.y);243mul(*lambda, *Ru, *Rz);244} else {245*lambda = (P.y - Q.y) / (P.x - Q.x);246}247}248249// lambda = slope of line between P,Q(,and -R)250251// x(R) = lambda^2 - x(P) - x(Q)252sqr(*Ru, *lambda);253sub(*Rz, *Ru, P.x);254sub(*Rx, *Rz, Q.x);255256// y(R) = lambda*(x(P) - x(R)) - y(P) = -(lambda*(x(R)-x(P))+y(P))257sub(*Ru, P.x, *Rx);258mul(*Rz, *Ru, *lambda);259sub(*Ry, *Rz, P.y);260261// set coordinates of R according to above262R.set(*Rx, *Ry);263}264265void neg(ellpoint_pE& Q, const ellpoint_pE& P)266{267Q.set(P.x, -P.y);268}269270// P - Q = R271272void sub(ellpoint_pE& R, const ellpoint_pE& P, const ellpoint_pE& Q)273{274static ellpoint_pE T;275neg(T, Q);276add(R, P, T);277}278279// addition/subtraction280ellpoint_pE operator+(const ellpoint_pE& P, const ellpoint_pE& Q)281{ ellpoint_pE x; add(x, P, Q); return x; }282283ellpoint_pE operator-(const ellpoint_pE& P, const ellpoint_pE& Q)284{ ellpoint_pE x; sub(x, P, Q); return x; }285286// negation287ellpoint_pE operator-(const ellpoint_pE& P)288{ ellpoint_pE x; x = P; if (x.identity_f == 0) x.y = -x.y; return x; }289290ellpoint_pE& operator+=(ellpoint_pE& P, const ellpoint_pE& Q)291{ add(P, P, Q); return P; }292293ellpoint_pE& operator-=(ellpoint_pE& P, const ellpoint_pE& Q)294{ sub(P, P, Q); return P; }295296ellpoint_pE operator*(int m, const ellpoint_pE& P)297{298static ellpoint_pE *result = NULL, *Q = NULL;299static long q = -1;300long i;301302// reallocate as infrequently as possible303if (ell_pEInfo->q != q) {304q = ell_pEInfo->q;305if (result != NULL) {306delete result;307delete Q;308}309// NB: the only way the following are deallocated is if they310// are replaced during a later invocation (see previous311// lines), so technically this is a tiny memory leak312result = new ellpoint_pE;313Q = new ellpoint_pE;314} else315result->set();316317if (m == 0) return *result;318if (m < 0) return (-m)*P;319320for (i = 0, *Q = P; (1<<i) <= m; i++) {321if (m & (1<<i))322*result += *Q;323*Q += *Q;324}325326return *result;327}328329int is_zero(const ellpoint_pE& P) { return P.identity_f; }330331// *slow* but safe routine for computing order of a point332333long ellpoint_pE::order()334{335ellpoint_pE Q;336long o;337338// compute multiples of self until find 0339Q = *this;340for (o = 1; !is_zero(Q); o++)341Q += *this;342343return o;344}345346//////////////////////////////347348unsigned long* to_ulong(ellpoint_pE& P)349{350static unsigned long result[2];351if (is_zero(P)) return NULL; // identity352result[0] = to_ulong(P.x);353result[1] = to_ulong(P.y);354return result;355}356357//////////////////////////////358359long operator==(const ellpoint_pE& P, const ellpoint_pE& Q)360{361if (is_zero(P) && is_zero(Q)) return 1;362if (is_zero(P) || is_zero(Q)) return 0;363return (P.x == Q.x) && (P.y == Q.y);364}365366long operator<( ellpoint_pE& P, ellpoint_pE& Q)367{368// O < anything else369if (is_zero(P)) return 1;370if (is_zero(Q)) return 0;371372#if X_ORDER373if (P.x < Q.x) return 1;374if (P.x == Q.x && P.y < Q.y) return 1;375#else376if (P.y < Q.y) return 1;377if (P.y == Q.y && P.x < Q.x) return 1;378#endif379return 0;380}381382long operator<=(ellpoint_pE& P, ellpoint_pE& Q)383{ return (P < Q) || (P == Q); }384385long operator>( ellpoint_pE& P, ellpoint_pE& Q)386{ return (Q <= P); }387388long operator>=(ellpoint_pE& P, ellpoint_pE& Q)389{ return (Q < P); }390391ostream& operator<<(ostream& s, const ellpoint_pE& P)392{393if (P.identity_f)394s << "0";395else396s << "(" << P.x << ", " << P.y << ")";397return s;398}399400//////////////////////////////401//402// routines for calculating group order403404///////////////////////////////////////////////////////////////////////////405406// binary tree code407// - used for storing points in bsgs algorithm408409struct node {410unsigned long point[2];411long i; // logarithm with respect to some point412struct node *left;413struct node *right;414};415416typedef struct node node_t;417typedef struct node *node_p;418419// compare two nodes420421int cmp(unsigned long *a, unsigned long *b) {422if (a[0] == b[0] && a[1] == b[1]) return 0;423if (a[0] < b[0]) return -1;424if (a[0] > b[0]) return 1;425if (a[1] < b[1]) return -1;426return 1;427}428429// look to see if node lies in tree430431static long lookup(struct node* node, unsigned long *target) {432if (node == NULL)433return(-1);434else {435int r = cmp(node->point, target);436if (r == 0)437return(node->i);438else {439if (r > 0)440return(lookup(node->left, target));441else442return(lookup(node->right, target));443}444}445}446447// add node to tree448449void insert(node_p head, node_p node)450{451node->left = node->right = NULL;452453if (node == head)454return;455456int r = cmp(node->point, head->point);457458if (r<0) {459if (head->left == NULL)460head->left = node;461else462insert(head->left, node);463} else {464if (head->right == NULL)465head->right = node;466else467insert(head->right, node);468}469}470471///////////////////////////////////////////////////////////////////////////472//473// baby-step giant-step code474475// - used to find an annihilator of a point on an elliptic curve476//477// input:478// P : point on elliptic curve479// M : number of baby steps480// N : anchor point for giant step481//482// sketch:483//484// (1) compute [i]P for i=1..M-1 and return i if find [i]P=O485//486// (2) compute [N-M^2+j*M]P for j=0..M-1 and return (N-M^2+j*M-i) if487// [i]P = [N-M^2+j*M]P488//489// (3) return -1490//491// - if terminates in (1), will return smallest possible i492// - if terminates in (2), will return smallest possible j493// - a return value of -1 implies the search failed494// - if called with N=M^2+1 and returns r>0, then r=order(P)495496static node_t *bsgs_annihilate_nodes = NULL;497498long bsgs_annihilate(const ellpoint_pE& P, const long M, const long N)499{500static ellpoint_pE *R = NULL;501static long q = -1, max_M = -1;502node_t *nodes = bsgs_annihilate_nodes;503long i;504505// create workspace for storing multiples of P, if necessary506if (nodes == NULL || ell_pEInfo->q != q || max_M < M) {507// either need more workspace or have switched finite fields508509// clean up previous workspace, if necessary510if (nodes != NULL) {511delete nodes;512delete R;513}514515// NB: one can clean up the following allocation for nodes using516// ell_cleanup(), but the last copy of R used will always517// stick around518519// allocate new workspace520nodes = bsgs_annihilate_nodes = new(node_t[M]);521R = new ellpoint_pE;522assert(nodes != NULL && R != NULL);523524// keep track of values current workspace depends on525q = ell_pEInfo->q;526max_M = M;527}528529// baby steps: compute [i]P for i=1..M-1530// - obviously [0]P=O, hence the reason we don't consider i=0531static unsigned long *point;532*R = P;533for (long i=1; i < M; i++, *R += P) {534// claim: R = [i]P535536// convert R to convenient format for storing in a binary tree537point = to_ulong(*R);538if (point == NULL)539return i; // [i]*P = O540541// add R to our binary tree542// - not on tree already because otherwise R=[i]P=[j]P and543// hence [i-j]P=O, which we would have seen544memcpy(nodes[i-1].point, point, sizeof(unsigned long)*2);545nodes[i-1].i = i;546insert(nodes, nodes+(i-1));547548// NB: point points to statically allocated memory hence549// doesn't need to be freed550}551552// claim: R=[M]P553554// compute base point for giant steps555ellpoint_pE Q = (N-((M-1)*M))*P;556if (Q == *R)557return N-(M*M); // claim: order(P) | (N-M^2+M)-M = N^2-M558559// giant steps: compute Q + [M*i]P = for i=1..M560for (long j = M-1; j >= 0; j--, Q += *R) {561// claim: Q = [N-j*M]P562if (is_zero(Q))563return N-j*M;564565// check to see if point was computed as a baby step566if ((i=lookup(nodes, to_ulong(Q))) != -1)567return N-j*M-i; // [N-j*M]P = [i]P568569// don't do the last addition since we don't need it570if (j == 0)571break;572}573574// failed575return -1;576}577578// baby-step giant-step search for m in [0,M^2-1] so [m]P = Q579//580// input:581// P : base point on elliptic curve582// Q : point on to compute log_P for583// M : number of baby steps584//585// sketch:586//587// (1) compute Q-[i]P for i=0..M-1 and return i if find [i]P=Q588//589// (2) compute [j*M]P for j=1..M-1 and return (i+j*M) if Q-[i]P=[j*M]P590//591// (3) return -1592//593// - if terminates in (1), will return smallest possible i594// - if terminates in (2), will return smallest possible i+j*M595// - a return value of -1 implies the search failed596597static node_t *bsgs_log_nodes = NULL;598599long bsgs_log(const ellpoint_pE& P, const ellpoint_pE& Q, const long M)600{601static ellpoint_pE *R = NULL, *S = NULL;602static long q = -1, max_M = -1;603node_t *nodes = bsgs_log_nodes;604long i;605606// create workspace for storing multiples of P, if necessary607if(nodes == NULL || ell_pEInfo->q != q || max_M < M) {608// either need more workspace or have switched finite fields609610// clean up previous workspace, if necessary611if (nodes != NULL)612delete nodes;613if (R != NULL)614delete R;615if (S != NULL)616delete S;617618// NB: one can clean up the following allocation for nodes using619// ell_cleanup(), but the last copies of R,S used will always620// stick around621622// allocate new workspace623bsgs_log_nodes = nodes = new(node_t[M]);624R = new ellpoint_pE;625S = new ellpoint_pE;626assert(nodes != NULL && R != NULL && S != NULL);627628// keep track of values current workspace depends on629q = ell_pEInfo->q;630max_M = M;631}632633// baby steps: compute Q-[i]P for i=0,...,M-1634static unsigned long *point;635*R = Q;636for(long i = 0; i < M; i++, *R -= P) {637// claim: R = Q - [i]P638639// convert R to convenient format for storing in a binary tree640point = to_ulong(*R);641if (point == NULL)642return i; // Q = [i]P643644// add R to our binary tree645// - if on tree already, then R=Q-[i]P=Q-[j]P and [i-j]P=O, which646// implies we won't find k so [k]P=Q because necessarily k<i-j647// and we already noticed [k]P!=Q for all k<i648// - rather that noticing we're in previous case, we just keep649// running since we expect to call this routine only when the650// order(P)>M, hence it can't occur651// ==> we can assume [i]P!=[j]P for j<i when necessary652memcpy(nodes[i].point, point, sizeof(unsigned long)*2);653nodes[i].i = i;654insert(nodes, nodes+i);655656// NB: point points to statically allocated memory hence657// doesn't need to be freed658}659660// giant steps: compute S = [j*M]P for j=1..M-1661*R = M*P;662*S = *R;663for (long j = 1; j < M; j++, *S += *R) {664// S = [j*M]P665666// if [j*M]P==0, then Q does not lie in <P> because we already667// checked whether [k]P==Q for 0<k<j*M668if (is_zero(*S))669return -1; // [j*M]P = 0670671i = lookup(nodes, to_ulong(*S));672if (i != -1)673return i+j*M; // [j*M]P = Q - [i]P674675// don't need last addition, so skip it676if (j == M-1 )677break;678}679680// failed681return -1;682}683684///////////////////////////////////////////////////////////////////////////685686// compute group size of current elliptic curve687// - memoryless, so every invocation does calculation from scratch!!688689long ell_pE::order()690{691static zz_pE x, y, y_sqr, z;692static ellpoint_pE P, Q, R, S;693long a_tau_q = 0, q = ell_pEInfo->q;694695// for really small finite fields, do stupid brute-force count696if (q <= 36) {697x = 0;698do {699// y^ = (x^2 + a4)*x + a6;700sqr(z, x);701add(y_sqr, z, ell_pEInfo->a4);702mul(z, y_sqr, x);703add(y_sqr, z, ell_pEInfo->a6);704705// if y^2=0, then trace contribution is 0706if (y_sqr == 0)707continue;708709// trace contribution is -chi(x^3+a4*x+a6)710// - note, this is trace on H^1, hence the minus sign711if (zz_pEExtraInfo->legendre_char(y_sqr) == 1)712a_tau_q += -1;713else714a_tau_q += +1;715} while(inc(x) == NO_CARRY);716717return (q+1-a_tau_q);718}719720// for larger fields, generate one or more points so can determine721// order of group as the unique integer in the Hasse-Weil interval722// [q+1-2*sqrt(q),q+1+2*sqrt(q)] satisfying certain properties.723//724// - in general it suffices to find P so there is a unique multiple of725// of |<P>|726// - might need to find Q so there's a unique multiple of |<P,Q>|727728long long_two_sqrt_q = (long)floor(2.0*sqrt((double)q));729while ((long_two_sqrt_q+1)*(long_two_sqrt_q+1) <= 2*q)730long_two_sqrt_q++;731732// integral form of H-W interval733long long_l, long_r;734long_l = q+1-long_two_sqrt_q;735long_r = q+1+long_two_sqrt_q;736737long HW_int = long_r-long_l;738long M = (long)ceil(sqrt(HW_int+1));739long sqrt_l = (long)ceil(sqrt(long_l));740741long order_P = -1, order_Q, sqrt_order_P;742unsigned long ul;743744x = 0; y = 0; z = 0; y_sqr = 0;745do {746// step 1: see if there is R in E(F_q) so x(R)=x747748// y^2 = (x^2+a4)*x + a6749// - a priori, y lives in F_{q^2} and we only know y^2750sqr(z, x);751add(y_sqr, z, ell_pEInfo->a4);752mul(y, y_sqr, x);753add(y_sqr, y, ell_pEInfo->a6);754755// check to see if y lives in F_q756ul = to_ulong(y_sqr);757if (y_sqr != 0 && zz_pEExtraInfo->legendre_char(y_sqr) == 0)758continue; // nope, so no such point759760// R=(x,y) is on our curve761y = (y_sqr == 0) ? y_sqr : zz_pEExtraInfo->square_root(y_sqr);762R.set(x, y);763assert(R.on_curve());764765// step 2: check to see if order(R) <= r-l <= M^2766long order_R = bsgs_annihilate(R, M, M*M+1);767if (order_R == -1 || order_R > HW_int) {768// order(R) > r-l so there is a unique multiple in HW interval769order_P = bsgs_annihilate(R, M, long_l + M*M);770assert(order_P > 0 && order_P <= long_r);771return order_P;772}773774// step 3: if is first time we made it this far, note the point775if (order_P == -1) {776P = R; order_P = order_R;777order_Q = -1;778continue; // continue looking for more points779}780781// step 4: if new point has higher order than recorded point, swap782if (order_R > order_P) { // && order_Q == -1) {783Q = P; order_Q = order_P;784P = R; order_P = order_R;785} else {786Q = R; order_Q = order_R;787}788sqrt_order_P = (long)ceil(sqrt(order_P));789790// step 5: check if order(P) >= sqrt(l)791// - if P has maximal order, this will be satisfied792if (order_P < sqrt_l)793continue; // no, so continue looking for R which works794795// step 6: determine which multiples of order_P lie in HW interval796//797//798// - is at least one; corresponds to |G|799800// step 6.a: find smallest and largest multiples801long min_d = (long)floor(long_l/order_P) - 1, max_d;802while ((q+1-order_P*min_d)*(q+1-order_P*min_d) > 4*q)803min_d++;804max_d = min_d;805while ((q+1-order_P*max_d)*(q+1-order_P*max_d) <= 4*q)806max_d++;807max_d--;808809// claim: if order_P >= sqrt(q+1-2*sqrt(q)) and q>36, then810// #multiples <= 1 + (4*sqrt(q)+1)/order_P < 6811assert(max_d - min_d < 5);812813// step 6.b: check if had exactly one multiple814if (min_d == max_d)815return min_d*order_P;816817// prop: q>34 and |<P,Q>| >= q+1-2*sqrt(q) ==> |<P,Q>|/order_P > 4818//819// pf:820// - let n = max_d-min_d821//822// ==> n*order(P) = (max_d-min_d)*order(P) <= 4*sqrt(q)823// ==> min_d >= (q+1-2*sqrt(q))/order(P)824// >= n*(q+1-2*sqrt(q))/(4*sqrt(q))825//826// - q > 34, so min_d >= n*(q+1-2*sqrt(q))/(4*sqrt(q)) > n827//828// - gcd(d1,d2) <= n for all d1<d2 in [min_d,max_d]829//830// ==> suffices to show there is a unique d in [min_d,max_d] so831// d*Q lies in <P>832//833// - let i = order(Q mod P) = |<P,Q>|/|<P>|834//835// - if |<P,Q>| lies in H-W, then i >= min_d > n836//837// - if d1*Q,d2*Q are both in <P> for d1<=d2 in [min_d,max_d],838// then i|gcd(d1,d2), hence d1=d2 or |<P,Q>|<q+1-2*sqrt(q)839840// step 7: check if m*Q lies in <P> for m=1,...,4841// - prop implies we can find P,Q so this doesn't happen842int num_d = max_d - min_d + 1;843if (bsgs_log(P, Q, sqrt_order_P) != -1)844continue;845if (num_d > 2 && order_Q % 2 == 0 && bsgs_log(P, 2*Q, sqrt_order_P) != -1)846continue;847if (num_d > 3 && order_Q % 3 == 0 && bsgs_log(P, 3*Q, sqrt_order_P) != -1)848continue;849if (num_d > 4 && order_Q % 4 == 0 && bsgs_log(P, 4*Q, sqrt_order_P) != -1)850continue;851852// step 8: determine if there is unique d in [min_d,max_d] so853// d*Q lies in <P>854long d, order_G = -1;855num_d = 0;856for (d = min_d; d <= max_d; d++) {857long log_P = bsgs_log(P, GCD(d, order_Q)*Q, sqrt_order_P);858if (log_P != -1) {859num_d++;860order_G = d*order_P;861}862}863assert(num_d == 1);864865return order_G;866} while(inc(x) == NO_CARRY /*&& test < MAX_POINT_TEST*/);867868// should never get here!869assert(0);870871return -1;872}873874void ell_cleanup()875{876if (bsgs_annihilate_nodes != NULL) {877delete bsgs_annihilate_nodes;878bsgs_annihilate_nodes = NULL;879}880if (bsgs_log_nodes != NULL) {881delete bsgs_log_nodes;882bsgs_log_nodes = NULL;883}884}885886//////////////////////////////887888#ifdef MAIN889int main(int argc, char **argv)890{891long p = 101, d = 1, q, i;892893if (argc == 2)894d = atoi(argv[1]);895896init_NTL_ff(p, d, 0, 0, 0);897q = to_ulong(zz_pE::cardinality());898assert(q < (1L << 28));899900// precompute list of square roots901zz_pE sqr_roots[q], x, x2;902do {903x2 = x*x;904sqr_roots[to_ulong(x2)] = x;905} while (inc(x) == NO_CARRY);906907#if 0908{909zz_pE X;910911X = p-1;912inc(X);913cout << "X = " << X << "\n";914cout << "X^2 = " << (X^2) << "\n";915}916#endif917918zz_pE one;919920// initialize the curve y^2 = x^3 + x + 1921one = 1;922ell_pE::init(one /*a4*/, one /*a6*/);923924zz_pE y, y2;925926// brute force points on curve (mostly in pairs) by trying every x927// and using model y^2 = x^3 + x + 1928do {929// try to compute square root of x^3 + x + 1930y2 = (sqr(x) + ell_pEInfo->a4)*x + ell_pEInfo->a6;931y = sqr_roots[to_ulong(y2)];932933// zero entry in table with y2 = 0 implies no square root934if (y2 != 0 && y == 0)935continue; // not a square936937cout << x << " " << y << " ";938939// initialize point on current curve940ellpoint_pE P(x,y);941942assert(P.on_curve());943cout << P << " ";944945long o = P.order();946cout << o << "\n";947948// make sure that [o]P = O949ellpoint_pE Q;950Q = o*P;951assert(is_zero(Q));952953// extra sanity check. make sure [i]P != O for 0 < i < o954long i;955for (i = 1; i < o; i++)956assert(!is_zero(i*P));957} while (inc(x) == NO_CARRY);958959return 0;960}961#endif // MAIN962963964