#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 rom_mid_exp(integrand_function func,
			 double a,
			 double b,
			 void *c,
			 int nc,
			 double eps,
			 double size)
{
 double *xv, *yv;
 double s[MAXPASS], h[MAXPASS];
 double ss = quiet_nan(0), dss;

 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);
	}

 assert(MAXPASS > MINPASS);
 int pass, nval;
 for(pass=0,nval=2;pass<MAXPASS;pass++,nval=(nval-1)*2+1)	{
	xv = new double[nval];
	yv = new double[nval];
	assert(xv && yv);

	/* fprintf(stderr, "nval=%d on pass %d\n", nval, pass); */

	int i;
	for(i=0;i<nval;i++)	{
		xv[i] = b*(i+0.5)/nval + a*(nval-0.5-i)/nval;
		yv[i] = (*func)(xv[i], c, nc);
		}

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

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

	// End parts:
	s[pass] += ((xv[nval-2]-xv[nval-1])/(log(yv[nval-2])-log(yv[nval-1])))
					* yv[nval-1]
					* (1-pow(yv[nval-2]/yv[nval-1],
								(b-xv[nval-1])/(xv[nval-2]-xv[nval-1])));

	s[pass] += ((xv[1]-xv[0])/(log(yv[1])-log(yv[0])))
					* yv[0] * (1-pow(yv[1]/yv[0], (a-xv[0])/(xv[1]-xv[0])));

	h[pass] = 1.0/(double)(nval-1);
	/* fprintf(stderr, "h=%g, s=%g\n", h[pass], s[pass]); */
	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);
		/* fprintf(stderr, "ss=%g with nuse=%d\n", ss, nuse); */
		if(fabs(dss) <= (nuse>=OKPASS ? eps : eps/10) *
										(fabs(ss)+fabs(size)))	{
			delete xv;
			delete yv;
			return ss;
			}
		}

 	delete xv;
 	delete yv;
	}

 printf("romexp: too many steps:accuracy=%g\n", dss);
 fprintf(stderr, "romexp: too many steps:accuracy=%g\n", dss);

 return ss;
}
