/*
 *  Algebraic operations on Elliptic Curve Group
 *	(Part of SKS cryptosystem)
 *
 * 
 * 	Copyright (C) 2004-2007  Manuel Pancorbo Castro
 * 
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version 2
 *	of the License, or (at your option) any later version.
 * 
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 * 
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *	Manuel Pancorbo Castro <mpancorbo@gmail.com>
 * 
 * 
 */
 
#include <assert.h>
#include "eclib.h"

int ecCheck (/*const*/ ec_point p)
	/* confirm that y^2 + x*y + (EC_A x^2) = x^3 + EC_B for point p */
	/* returns 1 if true (p belongs to curve) or 0 if false */
{
	gf_poly t1, t2, t3;
	
	assert(p != NULL);
	//assert(ecExists(p));
	#ifdef EC_A
	gf_poly a;
	//gfOpen(a);
	gfSet(a, EC_A);
	#endif
	
	//gfOpen(t3); gfOpen(t1); gfOpen(t2);

	gfSquare (t1, p->y);
	gfMultiply (t2, p->x, p->y);
	gfAdd (t1, t1, t2);	/* t1 := y^2 + x*y */
	gfSquare (t2, p->x); /* t2 = x^2 */
	#ifdef EC_A
	gfMultiply(t3, t2, a); /* t3 = EC_A x^2 */
	gfAdd(t1, t1, t3);		/* t1 = y^2 + x*y + EC_A x^2 */
	gfClear(a);
	#endif
		
	gfMultiply (t3, t2, p->x);
	gfSet(t2, EC_B);   /** t2 <- EC_B **/
	gfAdd (t2, t3, t2);	/** t2 := x^3 + EC_B */
	int ret = gfEqual (t1, t2);
	
	gfClear(t3), gfClear(t1), gfClear(t2);

	return ret;
} /* ecCheck */

#if 0
ec_point ec_Open()
{
	ec_point a;

	if( (a = malloc(sizeof(_ecpoint))) == NULL ) return NULL;
	//gfOpen(a->x);
	if( a->x == NULL ) return NULL;
	//gfOpen(a->y);
	if( a->y == NULL ) return NULL;
	
	return a;
}

ec_projective ec_ProjOpen()
{
	ec_projective a;

	if( (a = malloc(sizeof(_ecprojective))) == NULL ) return NULL;
	//gfOpen(a->x);
	if( a->x == NULL ) return NULL;
	//gfOpen(a->y);
	if( a->y == NULL ) return NULL;
	//gfOpen(a->z);
	if( a->z == NULL ) return NULL;
	
	return a;
}

#endif

void ecClear(ec_point a)
{
	assert(a != NULL);
	gfClear(a->x);
	gfClear(a->y);
	//free(a);
}

void ecProjClear(ec_projective a)
{
	assert(a != NULL);
	gfClear(a->x);
	gfClear(a->y);
	gfClear(a->z);
	//free(a);
}


int ecEqual (/*const*/ ec_point p, /*const*/ ec_point q)
	/* evaluates to 1 if p == q, otherwise 0 (or an error code) */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecExists(p) && ecExists(q));
	return gfEqual (p->x, q->x) && gfEqual (p->y, q->y);
} /* ecEqual */

void ecZero(ec_point p)
	/* sets p to the point at infinity O, clearing entirely the content of p */
{
	assert(p != NULL);
	//assert(ecExists(p));
	gfZero (p->x);
	gfZero (p->y);
} /* ecZero */


void ecCopy (ec_point p, /*const*/ ec_point q)
	/* sets p := q */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecExists(p) && ecExists(q));
	gfCopy (p->x, q->x);
	gfCopy (p->y, q->y);
} /* ecCopy */

void ecProj2Affine(ec_point p, /* const */ ec_projective q)
	/* Changes projective coordinates (q) to affine (p) p <- q */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecExists(p) && ecProjExists(q));
	
	gf_poly z2;	
	if(gf_iszero(q->z)){	/** Infinite point **/
		gfZero(p->x);
		gfZero(p->y);
	}
	else{
		//gfOpen(z2); 
		gfSquare(z2, q->z);
		gfDivide(p->x, q->x, q->z);
		gfDivide(p->y, q->y, z2);
		gfClear(z2);
	}
} /* ecProj2Affine */

void ecAffine2Proj(ec_projective q, /* const */ ec_point p )
	/* Changes affine coordinates (p) to projective (q) q <- p */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecExists(p) && ecProjExists(q));
	gfCopy(q->x, p->x);
	gfCopy(q->y, p->y);
	gfSet(q->z, 1);
	
} /* ecAffine2Proj */

ERROR ecCalcY (ec_point p, int ybit)
	/* given the x coordinate of p, evaluate y such that y^2 + x*y = x^3 + EC_B */
{
	gf_poly a, b, t;
	
	assert(p != NULL);
	//assert(ecExists(p));
	//gfOpen(a); gfOpen(b); gfOpen(t);
	gfSet(b, EC_B);
	if(gf_iszero(p->x)){
		/* elliptic equation reduces to y^2 = EC_B: */
		#ifdef EC_SQR_B
		gfSet(p->y, EC_SQR_B);
		#else
		gfSquareRoot (p->y, EC_B);
		#endif
		gfClear (a); gfClear (t); gfClear (b);
		return 0;
	}
	/* evaluate alpha = x^3 + b = (x^2)*x + EC_B: */
	gfSquare (t, p->x); /* keep t = x^2 for beta evaluation */

	gfMultiply (a, t, p->x);
	gfAdd (a, a, b); /* now a == alpha */
	if(gf_iszero(a)){
		gfZero(p->y);
		gfClear (a); gfClear (t); gfClear (b);
		return 0;
	}
	/* evaluate beta = alpha/x^2 = x + EC_B/x^2 + (EC_A) */
	/*gfInvert(a, t);
	gfMultiply(a, b, a);*/
	gfDivide(a, b, t);
	#ifdef EC_A
	gfSmallAdd(a, EC_A);
	#endif
	gfAdd (a, p->x, a); /* now a == beta */
	
	/* check if a solution exists: */
	if (gfTrace (a) != 0) {
		/* destroy potentially sensitive data: */
		gfClear (a); gfClear (t); gfClear (b);
		return 1; /* no solution */
	}
	/* solve equation t^2 + t + beta = 0 so that gfYbit(t) == ybit: */
	if(gfQuadSolve (t, a)) return 0;
	if (gfYbit (t) != ybit) {
		gfSet(a, 1);
		gfAdd(t, a, t);
	}
	/* compute y = x*t: */
	gfMultiply (p->y, p->x, t);

	/* destroy potentially sensitive data: */
	gfClear (a); gfClear (t); gfClear (b); 
	return 0;
} /* ecCalcY */

void ecRandom (ec_point p)
	/* sets p to a random point of the elliptic curve defined by y^2 + x*y = x^3 + EC_B */
	/* Only for testing purposes */
{
	int check;
	assert(p != NULL);
	//assert(ecExists(p));
	do {
		/* generate a pseudo-random x component: */
		gfRandom (p->x);
		/* evaluate the corresponding y component: */
		check = ecCalcY (p, 0);
#ifdef CHECK_POINT_DERIVATION
		if (ecCheck (p)) {
			printf (">>> invalid elliptic curve point <<<\n");
			return;
		}
#endif /* ?CHECK_POINT_DERIVATION */
	} while (check);
#ifdef CHECK_POINT_DERIVATION
	printf("point OK\n");
#endif

} /* ecRandom */

int ecYbit (/*const*/ ec_point p)
	/* evaluates to 0 if p->x == 0, otherwise to gfYbit (p->y / p->x) */
{
	gf_poly t1, t2;
	assert(p != NULL);
	//assert(ecExists(p));
	if (gf_iszero(p->x)) {
		return 0;
	} else {
		//gfOpen(t1); gfOpen(t2);
		/*gfInvert (t1, p->x);
		gfMultiply (t2, p->y, t1);*/
		gfDivide(t2, p->y, p->x);
		return gfYbit (t2);
		gfClear(t1); gfClear(t2);
	}
} /* ecYbit */

void ecProjDouble (ec_projective p)
	/* sets p := 2*p in projective coordinates */
{
	assert(p != NULL);
	//assert(ecProjExists(p));
	if(gf_iszero(p->x) && gf_iszero(p->y) /*&& gf_iszero(p->z)*/) return;

	gf_poly z2, bz4;
	//gfOpen(z2); gfOpen(bz4);
	
	gfSquare(z2, p->z);
	gfSquare(p->x, p->x);
	gfMultiply(p->z, p->x, z2);	/** Z <- Z^2 * X^2 **/
	gfSquare(bz4, z2);
	
	gfZero(z2); gfSmallAdd(z2, EC_B);
	gfMultiply(bz4, bz4, z2);	/** bz4 = EC_B * Z^4 **/
	gfSquare(p->x, p->x);	
	gfAdd(p->x, p->x, bz4);		/** X <- X^4 + bz4 **/
	
	gfSquare(p->y, p->y);
	gfAdd(p->y, p->y, bz4);
	#ifdef EC_A		/** A == 1 **/
	gfAdd(p->y, p->y, p->z);
	#endif
	gfMultiply(p->y, p->y, p->x);	/** X * (EC_A * Z + Y^2 + bz4) **/
	gfMultiply(bz4, bz4, p->z);
	gfAdd(p->y, p->y, bz4);		/** Y <- Z * bz4 + X * (EC_A * Z + Y^2 + bz4) **/
	
	gfClear(z2), gfClear(bz4);
}

void ecProjAdd (ec_projective p, /*const*/ ec_point q)
	/* sets p := p + q in projective coordinates. It is assumed q.z = 1 */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecProjExists(p) && ecExists(q));
	
	/* first check if there is indeed work to do (q == 0): */
	if (gf_iszero(q->x) && gf_iszero(q->y)) return;
	
	/* if p == 0 */
	if (gf_iszero(p->x) && gf_iszero(p->y)){
		ecAffine2Proj(p, q);
		return;
	}
	
	gf_poly A, B, z2, C, D, E, F, G;
	
	//gfOpen(A); gfOpen(B); gfOpen(z2);
	gfSquare(z2, p->z);
	gfMultiply(A, q->y, z2);
	gfAdd(A, A, p->y);	/* A = Y1 Z0^2 + Y0 */
	
	gfMultiply(B, q->x, p->z);
	gfAdd(B, B, p->x);	/* B = X1 Z0 + X0 */
	
	if(gf_iszero(B)){
		if(gf_iszero(A)){ /** A = B = 0 The same point. Double it! **/
			ecProjDouble(p); 
		}
		else{			/** B = 0. The opposite point. Result is infinite **/
			gfZero(p->x);
			gfZero(p->y);
			gfSet(p->z, 1);
		}
	} 
	else{
		//gfOpen(C); gfOpen(D);
		gfMultiply(C, p->z, B);	/* C = B Z0 */
		#ifdef EC_A /** A == 1 **/
		gfAdd(D, C, z2);
		#else
		gfCopy(D, C);
		#endif
		gfSquare(B, B); 
		gfMultiply(D, D, B); /* D = B^2 (C + a Z0^2) */
		
		gfSquare(p->z, C);	/* Z2 = C^2 */
		
//		E = z2;
		gfMultiply(E, A, C); /* E = A C */
		gfSquare(p->x, A);
		gfAdd(p->x, p->x, D);
		gfAdd(p->x, p->x, E);	/* X2 = A^2 + D + E */
		
		//F = A; G = B;
		gfMultiply(F, q->x, p->z);
		gfAdd(F, F, p->x);	/* F = X2 + X1 Z2 */
		
		gfMultiply(G, q->y, p->z);
		gfAdd(G, G, p->x);	/* G = X2 + Y1 Z2 */
		
		gfMultiply(G, G, p->z);
		gfMultiply(p->y, E, F);
		gfAdd(p->y, p->y, G); /* Y2 = E F + G Z2 */
		gfClear(C); gfClear(D);
	}
	
	gfClear(A); gfClear(B); gfClear(z2);
	gfClear(E);gfClear(G);gfClear(F);
}

void ecDouble (ec_point p)
	/* sets p := 2*p */
{
	gf_poly lambda, t1, t2;
	/*gf_poly a;*/
	/*short int A;*/
	assert(p != NULL);
	//assert(ecExists(p));
	if(gf_iszero(p->x) && gf_iszero(p->y)) return;
	/*a->K = 1; a->P[0] =  A = EC_A; */
	/* evaluate lambda = x + y/x: */
	
	//gfOpen(lambda); gfOpen(t1); gfOpen(t2);
	
	/*gfInvert (t1, p->x);
	gfMultiply (lambda, p->y, t1);*/
	gfDivide(lambda, p->y, p->x);
	
	gfAdd (lambda, lambda, p->x);
	/* evaluate x3 = lambda^2 + lambda + (EC_A): */
	gfSquare (t1, lambda);
	#ifdef EC_A
	gfSmallAdd(t1, EC_A);	
	#endif
	gfAdd (t1, t1, lambda); /* now t1 = x3 */
	/* evaluate y3 = x^2 + lambda*x3 + x3: */
	gfSquare (p->y, p->x);
	gfMultiply (t2, lambda, t1);
	gfAdd (p->y, p->y, t2);
	gfAdd (p->y, p->y, t1);
	/* deposit the value of x3: */
	gfCopy (p->x, t1);
	gfClear(lambda); gfClear(t1); gfClear(t2);
} /* ecDouble */

void ecAdd (ec_point p, /*const*/ ec_point q)
	/* sets p := p + q */
{
	assert( (p != NULL) && (q != NULL) );
	//assert(ecExists(p) && ecExists(q));
	
	/* first check if there is indeed work to do (q != 0): */
	if (!gf_iszero(q->x) || !gf_iszero(q->y)) {
		if (!gf_iszero(p->x) || !gf_iszero(p->y)) {
			/* p != 0 and q != 0 */
			if (gfEqual (p->x, q->x)) {
				/* either p == q or p == -q: */
				if (gfEqual (p->y, q->y)) {
					/* points are equal; double p: */
					ecDouble (p);
				} else {
					/* must be inverse: result is zero */
					/* (should assert that q->y = p->x + p->y) */
					ecZero(p);
				}
			} 
			else {
				/* p != 0, q != 0, p != q, p != -q */
				gf_poly lambda, t, tx, ty, x3;
				//gfOpen(lambda); gfOpen(t); gfOpen(tx);
				//gfOpen(ty); gfOpen(x3);
				/* evaluate lambda = (y1 + y2)/(x1 + x2): */
				gfAdd (ty, p->y, q->y);
				gfAdd (tx, p->x, q->x);
				
				/*gfInvert (t, tx);
				gfMultiply (lambda, ty, t);*/
				gfDivide(lambda, ty, tx);
				
				/* evaluate x3 = lambda^2 + lambda + x1 + x2 + (EC_A): */
				gfSquare (x3, lambda);
				gfAdd (x3, x3, lambda);
				gfAdd (x3, x3, tx);
				#ifdef EC_A
				gfSmallAdd(x3, EC_A);
				#endif
				/* evaluate y3 = lambda*(x1 + x3) + x3 + y1: */
				gfAdd (tx, p->x, x3);
				gfMultiply (t, lambda, tx);
				gfAdd (t, t, x3);
				gfAdd (p->y, t, p->y);
				/* deposit the value of x3: */
				gfCopy (p->x, x3);
				
				gfClear(lambda); gfClear(t); gfClear(tx);
				gfClear(ty); gfClear(x3);
			}
		} 
		else {
			/* just copy q into p: */
			gfCopy (p->x, q->x);
			gfCopy (p->y, q->y);
		}
	}
} /* ecAdd */

static int diff_vector(signed char *b, vl_number x )
{
	assert(b != NULL);
	
	int old_bit, actual_bit, i = 0;
	vl_number w;
	
	//vlOpen(w); 
	vlCopy(w, x);
	actual_bit = old_bit = gf_one(w);
	b[i++] = -actual_bit;
	
	do{
		gf2m_Div_x(w, w);
		actual_bit = gf_one(w);
		b[i++] = old_bit - actual_bit;
		old_bit = actual_bit;
	} while(!gf_iszero(w));
	vlClear(w);
		
	return i;
}

void ecMultiply (ec_point p, vl_number k)
	/* sets p := k*p 
	It uses projective coordinates 
	It uses Minimal Average Weight representations
	see:
	"A New Minimal Average Weight Representation for Left-to-Right Point 
	Multiplication Methods" M. Khabbazian and T.A. Gulliver
	http://eprint.iacr.org/2004/266.ps
	 */
#define MWIN_SIZE	8	/** Number of points of given representation **/
{
	assert( (p != NULL) && (k != NULL) );
	//assert(ecExists(p) && vlExists(k));
	
	if(gf_iszero(k)){
		ecZero(p);
		return;
	}
	
	_ecpoint pos_buf[MWIN_SIZE],	/** Table for positives 1,3,...2M-1 **/
	         neg_buf[MWIN_SIZE];	/** Table for negatives -1,-3,...-(2M-1) **/
	_ecpoint *pos = pos_buf, *neg = neg_buf;
	ec_point pp;
	register int i, j, t, v, last_one = 0, length;
	const int m2 = 2*MWIN_SIZE;
	
	/*** Precalculate values ***/
	//ecOpen(pp); 
	ecCopy(pp, p); ecDouble(pp);
	for(i = 0; i < MWIN_SIZE; ++i, ++pos, ++neg){
		//gfOpen(pos->x); gfOpen(neg->x);
		//gfOpen(pos->y); gfOpen(neg->y);
		if(!i){ 
			ecCopy(pos, p);
		}
		else{
			ecCopy(pos, pos - 1);
			ecAdd(pos, pp);
		}
		ecCopy(neg, pos);
		ecNegate(neg);
	}
	ecClear(pp); 
	
	/*** obtain vector with differences ***/
	signed char b[GF_M + 1];
	memset(b, 0, GF_M + 1);
	length = diff_vector(b, k);
	
	/*** start! ***/
	ec_projective r;
	
	//ecProjOpen(r); 
	gfZero(r->x); gfZero(r->y);
	gfSet(r->z, 1);
	pos = pos_buf; neg = neg_buf;
	
	for(i = length - 1; i >= 0; /***/){
		v = b[i];
		if(v) last_one = i;
		for(j = i -1; (j >= 0); --j){
			t = b[j];
			v *= 2;
			v += t;
			if(abs(v) > m2) break;
			else if(t) last_one = j;
		}
		v = b[i];
		
		for(j = i - 1; (j >= last_one); --j){
			v *= 2;
			v += b[j];
		}
		j = i - last_one + 1;
		while(j--) ecProjDouble(r);
		i = last_one - 1;
		last_one = 0;
		
		if(!v) break; /*** It shoudn't happen until end ***/
		j = (abs(v) - 1) / 2;
		if(v > 0)
			ecProjAdd(r, pos + j);
		else
			ecProjAdd(r, neg + j);
	}	
	ecProj2Affine(p, r);
	
	ecProjClear(r);
	memset(b, 0, GF_M + 1);
	
	for(i = 0; i < MWIN_SIZE; ++i, ++pos, ++neg){
		gfClear(pos->x);
		gfClear(neg->x);
		gfClear(pos->y);
		gfClear(neg->y);
	}	
	
	return;
} /* ecMultiply */

#if 0

#endif

void ecPack (/*const*/ ec_point p, ecPoint k )
	/* packs ec. point into array (compressed point) */
{
	assert( (p != NULL) && (k != NULL) );
	//assert(ecExists(p) );
	gf_poly tmp;
	
	memset(k, 0, GF_SIZE);
	if(gf_iszero(p->x))	return;
	
	//gfOpen(tmp);
	gfCopy(tmp, p->x);
	gf2m_Mul_x(tmp, tmp);
	*(tmp->dp) |= ecYbit(p);
	
	gfPack(tmp, k);
	gfClear(tmp);
}

ERROR ecUnpack ( ec_point p, /*const*/ ecPoint k )
	/* unpacks compressed ec. point from array into full ec. point */
{
	assert((p != NULL) && (k != NULL));
	//assert(ecExists(p));
	int ybit;
	
	gfUnpack(p->x, k);
	ybit = gfYbit(p->x);
	gf2m_Div_x(p->x, p->x);
	return ecCalcY(p, ybit);
}

void ecNegate (ec_point p)
	/* sets p := -p */
{
	gfAdd (p->y, p->x, p->y);
} /* ecNegate */


#ifdef ECLIB_TEST

#include <stdlib.h>
#include <time.h>


void ecSub (ec_point p, /*const*/ ec_point r)
	/* sets p := p - r */
{
	ec_point t;
	//ecOpen(t);

	gfCopy (t->x, r->x);
	gfAdd  (t->y, r->x, r->y);
	ecAdd (p, t);
	ecClear(t);
} /* ecSub */

main(int argc, char ** argv)
{
	int i, yb, nfail = 0, afail = 0, sfail = 0, dfail = 0, 
	cfail = 0, qfail = 0, pfail = 0, yfail = 0, 
	test_count;
	ec_point f, g, x, y;
	ec_projective xproj;
	vl_number m, n;
	clock_t elapsed = 0L;

	if(argc > 1){
		test_count = atoi(argv[1]);
	}
	else test_count = 10;
	gfInit();
	srand ((unsigned)(time(NULL) % 65521U));
	//ecOpen(f); ecOpen(g);
	//ecOpen(x); ecOpen(y);
	//ecProjOpen(xproj);
	
	i = 0;
	ecZero(f);
	do{
		gfSet(f->x, ++i);
	} while(ecCalcY(f, 0));
	fprintf(stderr, "Primer elemento de curva:\n");
	gfPrint(stderr, "F.x", f->x);
	gfPrint(stderr, "F.y", f->y);
	/*printf ("Tamaño ulong64: %d\n", sizeof(ulong64));*/
	printf ("Executing %d curve self tests GF(2^%d)...\n",
	 test_count, GF_M);
	/*elapsed = -clock ();*/
	for (i = 0; i < test_count; i++) {
		ecRandom (f);
		ecRandom (g); 
		/*gfSet(f->x, 7);
		ecCalcY(f, 0);
		gfSet(g->x, 0);
		ecCalcY(g, 0);*/
		/* y calculation test: */
		yb = ecYbit (f);
		ecZero (x);
		gfCopy (x->x, f->x);
		 
		if(ecCalcY (x, yb)) printf("Error cálculo y\n");
		if (!ecEqual (f, x)) {
			yfail++;
			printf ("Y calculation test #%d failed!\n", i); 
			gfPrint(stderr, "F.x", f->x);
			gfPrint(stderr, "X.x", x->x);
			fprintf(stderr, "\n");
			gfPrint(stderr, "F.y", f->y);
			gfPrint(stderr, "X.y", x->y);
		}
		/* addition test: f+g = g+f */
		ecCopy (x, f); ecAdd (x, g);
		ecCopy (y, g); ecAdd (y, f);
		if (!ecEqual (x, y)) {
			afail++;
			printf ("Addition test #%d failed!\n", i); 
		}
		/* projective addition test: f+g (proj) = f+g (affine) */
		ecAffine2Proj(xproj, f);
		ecProjAdd (xproj, g);
		ecProj2Affine(x, xproj);
		ecCopy (y, f); ecAdd (y, g);
		if (!ecEqual (x, y)) {
			sfail++;
			printf ("Projective addition test #%d failed!\n", i); 
			gfPrint(stderr, "X.x", x->x);
			gfPrint(stderr, "Y.x", y->x);
			
			gfPrint(stderr, "X.y", x->y);
			gfPrint(stderr, "Y.y", y->y);
			fprintf(stderr, "\n");
		}
		/* projective duplication test 2*f (proj) = 2*f (affine)*/
		ecAffine2Proj(xproj, f);
		ecProjDouble(xproj);
		ecProj2Affine(x, xproj);
		ecCopy(y, f); ecDouble(y);
		if (!ecEqual (x, y)) {
			dfail++;
			printf ("Projective duplication test #%d failed!\n", i);
			gfPrint(stderr, "X.x", x->x);
			gfPrint(stderr, "Y.x", y->x);
			
			gfPrint(stderr, "X.y", x->y);
			gfPrint(stderr, "Y.y", y->y);
			fprintf(stderr, "\n");
		
		}
		
		/* quadruplication test: 2*(2*f) = f + f + f + f */
		ecCopy (x, f); ecDouble (x); ecDouble (x);
		ecZero (y); ecAdd (y, f); ecAdd (y, f); ecAdd (y, f); ecAdd (y, f);
		if (!ecEqual (x, y)) {
			qfail++;
			printf ("Quadruplication test #%d failed!\n", i); 
		}
		/* scalar multiplication commutativity test: m*(n*f) = n*(m*f) */
		//vlOpen(n); vlOpen(m);
		vlRandom(n); vlRandom(m);
		/*vlSet(n, 266); vlSet(m, 2);*/
		/*gfPrint(stderr, "F.x", f->x);*/
		ecCopy (x, f);
		ecCopy (y, f);
		elapsed -= clock ();
		ecMultiply (x, n); ecMultiply (x, m);
		/*gfPrint(stderr, "X.x", x->x);*/
		ecMultiply (y, m); ecMultiply (y, n);
		/*gfPrint(stderr, "Y.x", y->x);*/
		elapsed += clock ();
		if (!ecEqual (x, y)) {
			cfail++;
			printf ("Conmutative test #%d failed!\n", i); 
			gfPrint(stderr, "X.x", x->x);
			gfPrint(stderr, "Y.x", y->x);
			fprintf(stderr, "\n");
			gfPrint(stderr, "X.y", x->y);
			gfPrint(stderr, "Y.y", y->y);
		}
		
		/* packing test: unpack (pack (f)) = f */
		ecPoint p;
		ecPack (f, p);
		ecUnpack (x, p);
		if (!ecEqual (f, x)) {
			pfail++;
			printf ("Packing test #%d failed!\n", i);
			gfPrint(stderr, "F.x", f->x);
			gfPrint(stderr, "X.x", x->x);
			fprintf(stderr, "\n");
			gfPrint(stderr, "F.y", f->y);
			gfPrint(stderr, "X.y", x->y);
		}
	}
	/*elapsed += clock ();*/
	/*gfClear(m), gfClear(n), gfClear(p);*/
	ecClear(f); ecClear(g);
	ecClear(x); ecClear(y);
	vlClear(m); vlClear(n);
	ecProjClear(xproj);
	gfQuit();
	printf (" done, scalar multiplication time: %.3f s\n",
		(float)elapsed/CLOCKS_PER_SEC/(test_count?4*test_count:4));
	if (nfail) printf ("---> %d negations failed <---\n", nfail);
	if (afail) printf ("---> %d additions failed <---\n", afail);
	if (sfail) printf ("---> %d projective additions failed <---\n", sfail);
	if (qfail) printf ("---> %d quadruplications failed <---\n", qfail);
	if (dfail) printf ("---> %d projective duplications failed <---\n", dfail);
	if (cfail) printf ("---> %d conmutations failed <---\n", cfail);
	if (yfail) printf ("---> %d y calculations failed <---\n", yfail);
	if (pfail) printf ("---> %d packings failed <---\n", pfail);
	return nfail || afail || sfail || qfail || cfail || yfail || pfail;
}
#endif /* ECLIB_TEST */
