#include <gpk_std.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "romberg.h"
#include "numerics.h"
#include "gpklib/ieee754.h"
#include <assert.h>

C static double BESTACC = 1e-7;
C static double TF = 0.17;
C static int MINPASS = 1;
C static int OKPASS = 3;
C static int KMAX = 7;
#define MAXPASS	15

/* romberg-like integration for functions that can be approximated
	well by exponentials of smooth things */

double romexp(integrand_function func,
			 double a,
			 double b,
			 void *c,
			 int nc,
			 double eps,
			 double size)
{
 double *xv[MAXPASS], *yv[MAXPASS];
 double s[MAXPASS], h[MAXPASS];
 double ss = quiet_nan(0), dss;

 assert(MAXPASS > MINPASS);
 assert(func);
 if(a == b)	return 0.0;
 if(eps < BESTACC)      {
	fprintf(stderr, "Eps (error limit) is too small in qromo = %g\n", eps);
	exit(4);
	}
 xv[0] = (double *)malloc(2*sizeof(double));
 yv[0] = (double *)malloc(2*sizeof(double));
 assert(xv[0] && yv[0]);

 xv[0][0] = a;
 yv[0][0] = (*func)(xv[0][0], c, nc);
 xv[0][1] = b;
 yv[0][1] = (*func)(xv[0][1], c, nc);
 assert(yv[0][0]>=0.0 && yv[0][1]>=0.0);

 h[0] = 1.0;
 s[0] = yv[0][1]>0&&yv[0][0]>0
		? (xv[0][1]-xv[0][0]) * (yv[0][1]-yv[0][0])
					/ (log(yv[0][1]) - log(yv[0][0]))
		: 0.0;
 

 {
 int pass, nval;
 for(pass=1,nval=3;pass<MAXPASS;pass++,nval=(nval-1)*2+1)	{
	int i;

	xv[pass] = (double *)malloc(nval*sizeof(double));
	yv[pass] = (double *)malloc(nval*sizeof(double));
	assert(xv[pass] && yv[pass]);


	for(i=0;i<nval;i+=2)	{
		xv[pass][i] = xv[pass-1][i/2];
		yv[pass][i] = yv[pass-1][i/2];
		}
	free((char *)( xv[pass-1]));
	free((char *)( yv[pass-1]));
	for(i=1;i<nval;i+=2)	{
		C double ym = yv[pass][i-1];
		C double yp = yv[pass][i+1];
		C double tweak = TF*(yp-ym)/(yp+ym);
		xv[pass][i] = (0.5+tweak)*xv[pass][i+1] +(0.5-tweak)*xv[pass][i-1];
		yv[pass][i] = (*func)(xv[pass][i], c, nc);
		assert(yv[pass][i] >= 0.0);
		}

	/*
	for(i=0;i<nval;i++)
		fprintf(stderr, "pass=%d, i=%d, x=%g, y=%g\n", pass, i, 
						xv[pass][i], yv[pass][i]);
*/

	for(i=1,s[pass]=0.0;i<nval;i++)
		s[pass] += yv[pass][i]>0&&yv[pass][i-1]>0
					? (xv[pass][i]-xv[pass][i-1]) * (yv[pass][i]-yv[pass][i-1])
						/ (log(yv[pass][i]) - log(yv[pass][i-1]))
					: 0.0;

	h[pass] = 1.0/(double)(nval-1);
	if(pass >= MINPASS)	{
		C int nuse = pass+1<KMAX ? pass+1 : KMAX;
		assert(pass-nuse+1>=0 && pass-nuse+1<=pass);
		ss = ratint(&h[pass-nuse+1], &s[pass-nuse+1], nuse, 0.0, &dss);
		if(fabs(dss) <= (nuse>=OKPASS ? eps : eps/10) *
										(fabs(ss)+fabs(size)))	{
 			free((char *)(xv[pass]));
 			free((char *)(yv[pass]));
			return ss;
			}
		}

	}

 free((char *)(xv[pass]));
 free((char *)(yv[pass]));
 }
 printf("romexp: too many steps:accuracy=%g\n", dss);
 fprintf(stderr, "romexp: too many steps:accuracy=%g\n", dss);

 return ss;
}
