#define testing 0
/*
 * compos performs the composition of two reduced_______ forms.
 * Thus we are entitled to assume that
 * |b| <= a <= c
 * and
 * a,b < 1e16
 */
#include "mp.h"
#include "form.h"
form
compos(f1,f2)
form f1, f2;
{
	form f3;
	mint m, r, y;
	mint yq, yr;
	mint v1, v2;
	mint d, d1, s, n;
	mint x1, x2, y1, y2;
	mint temp, temp1, temp2;
	mint zero;
	double dinv();
	double modf();

	zero = itom(0);
	n.high = s.high = 0;
	s.low = (f1.b.low + f2.b.low)*0.5;
	n.low = s.low - f2.b.low;
#if testing
	temp1 = msub(mult(f1.c,f1.a),mult(f2.c,f2.a));
	if(mcmp(temp1,mult(n,s)) != 0){
		printf("compos: unequal determinants!\n");
		tobort(5);
	}
#endif
	d.high = 0;
	x1.high = y1.high = 0;
	d.low = dinv(f1.a.low,f2.a.low,&x1.low,&y1.low);
	d1.high = 0;
	x2.high = 0;
	y2.high = 0;
	v1.high = v2.high = 0;
	if(d.low == 1){
		d1.low = 1;
		v1.low = f1.a.low;
		v2.low = f2.a.low;
		m = mult(y1,n);
	}else{
		d1.low = dinv(s.low,d.low,&x2.low,&y2.low);
		v1.low = f1.a.low/d1.low;
		v2.low = f2.a.low/d1.low;
		mdiv(mult(y1,y2), v1, &temp);
		temp1 = mult(temp, n);
		temp2 = mult(x2, f2.c);
		m = msub(temp1, temp2);
	}
	mdiv(m, v1, &r);
	if((r.low + r.low) > v1.low)
		r.low -= v1.low;
	else if((r.low+r.low) < -v1.low)
		r.low += v1.low;
	yq = mdiv(mult(r,r), v1, &yr);
	temp = mult(f2.c, d1);
	temp = madd(temp, mult(r,f2.b));
	y = madd(temp,mult(v2,yr));
	temp = mdiv(y, v1, &temp2);
	if(mcmp(temp2,zero) != 0) tobort(10);
	f3.a = mult(v1, v2);
	f3.b = madd(f2.b, smult(mult(v2,r),2));
	f3.c = madd(temp, mult(yq,v2));
	reduce(&f3);
	return(f3);;
}
void
reduce(f)
form *f;
{
	mint babs;
	mint zero, one;
	mint mtemp, k;
	mint ka;
	double dt1, dt2, dtemp;

	zero = itom(0);
	one = itom(1);
back:
	if(mcmp(f->a,f->c) > 0){
		mtemp = f->a;
		f->a = f->c;
		f->c = mtemp;
		f->b = mchs(f->b);
	}
	babs = f->b;
	if(mcmp(f->b,zero) < 0)
		babs = mchs(babs);
	if(mcmp(f->a,babs) >= 0){
		if((mcmp(f->a,f->c)==0) || (mcmp(f->a,babs)==0)){
			f->b = babs;
		}
		return;
	}
	dt1 = f->b.high*1e16 + f->b.low;
	dt2 = f->a.high*1e16 + f->a.low;
	modf(dt1/dt2, &dtemp);
	if(dtemp>0) dtemp += 1;
	else if(dtemp<0) dtemp -= 1;
	modf(dtemp/2, &dtemp);
	modf(dtemp/1e16, &k.high);
	k.low = dtemp - 1e16*k.high;
	ka = mult(k,f->a);
	f->b = msub(f->b,madd(ka,ka));
	f->c = msub(f->c,mult(k,madd(ka,f->b)));
	goto back;
}
tobort(n)
int n;
{
	printf("Error %d\n", n);
	abort();
}

form
formpow(f,h)
form f;
double h;
{
	form ftemp;
	int i,n;
	int bit[120];
	double junk;
	double temph;
	double modf();

	if(h == 0.){
		ftemp = f;
		ftemp.b = mchs(ftemp.b);
		ftemp = compos(f, ftemp);
		return(ftemp);
	}

	i = 0;
	temph = h;
	while(temph != 0){
		modf(temph/2, &junk);
		if(2.*junk != temph)
			bit[i++] = 1;
		else
			bit[i++] = 0;
		temph = junk;
	}
	n = i-1;

	ftemp = f;
	for(i=n; i>0; i--){
		ftemp = compos(ftemp, ftemp);
		if(bit[i-1] != 0){
			ftemp = compos(f, ftemp);
		}
	}
	return(ftemp);
}
void
formout(f)
form f;
{
	printf("(");
	mout1(f.a);
	printf(",");
	mout1(f.b);
	printf(",");
	mout1(f.c);
	printf(")");
	return;
}
