#include <stdcxx.hxx>
#include <romberg.h>
#include <assert.h>
#include <math.h>		/* for abs(double) */

C double EF = 0.5;

struct tmp	{
	void *p;
	integrand_function f;
	double eps, size;
	method_fcn how;
	C doublet *outer;
	C doublet (*inner)(double x, C doublet& outer, C void *p);
	};

LOCAL double g(double x, void *p, int nc)
{
 ENTER_FCN("g");
 assert(p);
 C struct tmp *tp = (C struct tmp *)p;
 assert(tp->inner && tp->outer && tp->f);
 C doublet ab = (*tp->inner)(x, *tp->outer, tp->p);
 MARK_LINE;
 return qromo(tp->f, ab.x, ab.y, tp->how, tp->p, nc, tp->eps, tp->size);
}

double
qromo2(integrand_function f, C doublet& outer,
			C doublet (*inner)(double x, C doublet& outer, C void *p),
			method_fcn how, void *c, int nc, double eps_o, double size_o,
			double eps_i, double size_i, method_fcn how_i)
{
 ENTER_FCN("qr2 (*inner)");
 struct tmp cc;
 assert(f && how && inner);
 cc.eps = eps_i!=0.0 ? eps_i : eps_o*EF;
 cc.size = size_i!=0.0 ? size_i : size_o/fabs(outer.x-outer.y);
 cc.p = c;	cc.outer = &outer;	cc.inner = inner;
 cc.how = how_i ? how_i : how;	cc.f = f;

 MARK_LINE;
 return qromo(g, outer.x, outer.y, how, &cc, nc, eps_o, size_o);
}

double
qromo(integrand_function f, C doublet& ab,
			 method_fcn how, void *c, int nc, double eps, double size)
{
 ENTER_FCN("qromo(doublet)");
 return qromo(f, ab.x, ab.y, how, c, nc, eps, size);
}



struct rect_in	{
	void *p;
	dbl_integrand_function f;
	C doublet in;
	rect_in(void *_p, C doublet& _in, dbl_integrand_function _f)
			: p(_p), in(_in), f(_f) {;}
	};

LOCAL doublet
in_rect(double, C doublet&, C void *p)
{
 ENTER_FCN("in_rect");
 assert(p);
 return ((C rect_in *)p)->in;
}

LOCAL double
f_wrap(double x, double y, void *p)
{
 ENTER_FCN("f_wrap");
 assert(p);
 C rect_in *pp = (C rect_in *)p;
 assert(pp->f);
 return (*(pp->f))(x, y, pp->p);
}

double
qromo_xy(dbl_integrand_function f,
			C doublet& inner, C doublet& outer,
			method_fcn how, void *c, double eps_o, double size_o,
			double eps_i, double size_i, method_fcn how_i)
{
 ENTER_FCN("qromo_xy(doublet)");
 rect_in b(c, inner, f);
 return qromo_xy(f_wrap, in_rect, outer, how, &b, eps_o, size_o,
					eps_i, size_i, how_i);
}

struct dtmp	{
	C void *p;
	double y;
	dbl_integrand_function f;
	};

struct ddtmp : public dtmp	{
	double eps, size;
	method_fcn how;
	C doublet *outer;
	C doublet (*inner)(double x, C doublet& outer, C void *p);
	};

LOCAL double
gg(double x, void *p, int)
{
 ENTER_FCN("GG");
 assert(p);
 C struct dtmp *tp = (C struct dtmp *)p;
 assert(tp->f);
 return (*(tp->f))(x, tp->y, tp->p);
}

LOCAL double
g2(double y, C void *p, int)
{
 ENTER_FCN("g2");
 assert(p);
 struct ddtmp *ttp = (struct ddtmp *)p;
 assert(ttp->inner && ttp->outer && ttp->how);
 ttp->y = y;
 C doublet in = (*(ttp->inner))(y, *(ttp->outer), ttp->p);
 MARK_LINE;
 return qromo(gg, in.x, in.y, ttp->how, ttp, 0, ttp->eps, ttp->size);
}


/* for double integrals of two-parameter functions */
/*	over arbitrary areas: */
double
qromo_xy(dbl_integrand_function f,
			C doublet (*inner)(double x, C doublet& outer, C void *p),
			C doublet& outer,
			method_fcn how, void *c, double eps_o, double size_o,
			double eps_i, double size_i, method_fcn how_i)
{
 ENTER_FCN("qromo_xy(*inner)");
 if(outer.x == outer.y)	return 0.0;
 struct ddtmp cc;
 MARK_LINE;
 cc.eps = eps_i!=0.0 ? eps_i : eps_o*EF;
 MARK_LINE;
 cc.size = size_i!=0.0 ? size_i : size_o/fabs(outer.x-outer.y);
 MARK_LINE;
 cc.p = c;	cc.outer = &outer;	cc.inner = inner;
 cc.how = how_i ? how_i : how;	cc.f = f;

 MARK_LINE;
 return qromo(g2, outer.x, outer.y, how, &cc, 0, eps_o, size_o);
}
