#include <math.h>
#include <float.h>
#include "gpklib/assert.h"
#include "gpklib/line_int.h"
#include "gpklib/m22.h"
#include "gpklib/m22v2.h"

#define D	if(0)

#define R	300

#ifndef DBL_EPSILON
# define DBL_EPSILON	1e-14
#endif

line_intersection::line_intersection(C class line& o, C class line& t)
 : alongline(FALSE), atinf(FALSE), not_ready(FALSE),
 	n_int(0), where(o), direction(o.angle())
{
 double t1, t2;

 		/* a single point */
 if(t1=o.dir()(0)*t.dir()(1), t2=o.dir()(1)*t.dir()(0),
					fabs(t1-t2) > R*fabs(t1+t2)*DBL_EPSILON+DBL_MIN*R) {
	n_int = 1;
	D printf("one int! t1=%30.20g, t2=%30.20g\n", t1, t2);
	C vector2 so_st = inverse(matrix22(-o.dir()(0), t.dir()(0),
										-o.dir()(1), t.dir()(1)))
						* (o.base()-t.base());
	_sa[0] = so_st(0);
	_sb[0] = so_st(1);
	ii[0] = o(so_st(0));
 	}
 else if(t1=(o.base()(0)-t.base()(0))*(o.dir()(1)-t.dir()(1)),
		 t2=(o.base()(1)-t.base()(1))*(o.dir()(0)-t.dir()(0)),
		 fabs(t1-t2)<R*fabs(t1+t2)*DBL_EPSILON+DBL_MIN*R)	{
	alongline = TRUE;
	n_int = -1;
	D printf("a line int! t1=%g, t2=%g\n", t1, t2);
	}
 else	{
 	atinf = TRUE;	/* lines are parallel */
	D printf("No int!\n");
	}
}

line_intersection::line_intersection()
: alongline(FALSE), atinf(FALSE), not_ready(TRUE),
	n_int(0), where(), direction(0)
{assert(invariant());}

BOOL line_intersection::invariant() C
{
 assert(not_ready == !(*this));
 assert(num_ints() == n_int);
 assert(atinf == at_inf());
 assert(line_int() == alongline);
 return !(*this)
 			? (num_ints()==0 && !at_inf() && !line_int())
			: (line_int()&&num_ints()<0&&!at_inf())
					|| (at_inf()&&num_ints()==0&&!line_int())
					|| (num_ints()>0 && !at_inf() && !line_int())
					|| no_int();
}

line_intersection::line_intersection(C class line& t, C class nline& o)
 : alongline(FALSE), atinf(FALSE), not_ready(FALSE),
 	n_int(0), where(t),
 	direction(t.angle())
{
 C double c2 = t.a()*o.q()(0) + t.b()*o.q()(1);
 C double f2 = fabs(t.a()*o.q()(0)) + fabs(t.b()*o.q()(1));
 C double c1 = t.a()*o.dir()(0) + t.b()*o.dir()(1);
 C double f1 = fabs(t.a()*o.dir()(0)) + fabs(t.b()*o.dir()(1));
 C double c0 = t.a()*o.base()(0) + t.b()*o.base()(1) + t.c();
 C double f0 = fabs(t.a()*o.base()(0)) + fabs(t.b()*o.base()(1))
 				+ fabs(t.c());

 C double det = c1*c1 - 4*c2*c0;

 if(det < 0)	{
	n_int = 0;
	D printf("li:det<0 -- no intersection\n");
	}
 else if(fabs(c2) > 2*DBL_MIN + 3*DBL_EPSILON*f2)	{
	C double rd = sqrt(det)*(c1<0 ? 1 : -1);
	_sb[0] = (-c1 + rd)/(2*c2);
	_sb[1] = (2*c0)/(-c1 + rd);
	ii[0] = o(_sb[0]);
	ii[1] = o(_sb[1]);
	_sa[0] = (t-ii[0]).p_nearest_to_origin();
	_sa[1] = (t-ii[1]).p_nearest_to_origin();
	n_int = 2;
	D printf("li:2 ints s=%g and s=%g\n", _sa[0], _sa[0]);
 	}
 else if(fabs(c1) > 2*DBL_MIN + 3*DBL_EPSILON*f1)	{	/* c2 == 0 */
	_sb[0] = -c0/c1;
	ii[0] = o(_sb[0]);
	_sa[0] = (t-ii[0]).p_nearest_to_origin();
	n_int = 1;
	D printf("li:1 int s=%g\n", _sa[0]);
 	}
 else if(fabs(c0) > 2*DBL_MIN + 3*DBL_EPSILON*f0)	{
	atinf = TRUE;
	D printf("li: parallel curves\n");
	}
 else	{
	n_int = -1;
	alongline = TRUE;
	_sb[0] = _sa[0] = 0.0;
	D printf("li: equal curves\n");
 	}
 assert(invariant());
}

line_intersection& line_intersection::operator=(C line_intersection& a)
{
 alongline = a.alongline;
 atinf=a.atinf;
 not_ready=a.not_ready;
 n_int = a.n_int;
 where = a.where;
 direction = a.direction;
 ii[0]=a.ii[0]; ii[1]=a.ii[1];
 _sa[0]=a._sa[0]; _sa[1]=a._sa[1];
 _sb[0]=a._sb[0]; _sb[1]=a._sb[1];
 assert(alongline ? n_int!=0 : TRUE);
 assert(atinf ? n_int==0 : TRUE);
 return *this;
}

line_intersection::line_intersection(C line_intersection& a)
: alongline(a.alongline), atinf(a.atinf), not_ready(a.not_ready),
	n_int(a.n_int), where(a.where), direction(a.direction)
{
 ii[0]=a.ii[0]; ii[1]=a.ii[1];
 _sa[0]=a._sa[0]; _sa[1]=a._sa[1];
 _sb[0]=a._sb[0]; _sb[1]=a._sb[1];
 assert(alongline ? n_int!=0 : TRUE);
 assert(atinf ? n_int==0 : TRUE);
}
