/*
 * util.c
 *
 *
 * Part of TREE-PUZZLE 5.2 (February 2005)
 *
 * (c) 2003-2005 by Heiko A. Schmidt, Korbinian Strimmer, and Arndt von Haeseler
 * (c) 1999-2003 by Heiko A. Schmidt, Korbinian Strimmer,
 *                  M. Vingron, and Arndt von Haeseler
 * (c) 1995-1999 by Korbinian Strimmer and Arndt von Haeseler
 *
 * All parts of the source except where indicated are distributed under
 * the GNU public licence.  See http://www.opensource.org for details.
 *
 * ($Id$)
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif


/******* debugging stuff - HAS ;-) ********/

int UUU1=0;
/* fprintf(stderr, "UUU1: %d (%s:%d)\n", UUU1++, __FILE__, __LINE__); */
/* fputid(stderr, 65); fprintf(stderr, " = 65 (%s:%d - 666)\n", __FILE__, __LINE__); */


/*
 * Use Scalable Parallel Random Number Generator
 * See sprng.cs.fsu.edu for details
 * from National Center for Supercomputing Applications (NCSA)
 * See www.ncsa.uiuc.edu
 */
#ifdef NOSPRNG
#   ifdef SPRNG
#      undef SPRNG
#   endif
#else
#   define SPRNG 1
#endif

#ifdef SPRNG
#  ifdef PARALLEL
#    define USE_MPI
#    ifndef EXTERN
#      define EXTERN extern
#    endif
#    include "ppuzzle.h"
#  endif
#  if 0
#    define SIMPLE_SPRNG
#  endif
#  include "sprng/sprng.h"
#endif



#include "util.h"

#define STDOUT     stdout
#ifndef PARALLEL             /* because printf() runs significantly faster */
                             /* than fprintf(stdout) on an Apple McIntosh  */
                             /* (HS) */
#       define FPRINTF    printf
#       define STDOUTFILE
#else
#       define FPRINTF    fprintf
#       define STDOUTFILE STDOUT,
	extern int PP_NumProcs;
	extern int PP_Myid;
        long int PP_randn;
        long int PP_rand;
	void PP_Finalize();
#endif


/**********************************
 * memory allocation error handler
 */

void maerror(char *message)
{
	fprintf(STDOUT, "\n\n\nUnable to proceed (lack of memory: %s)\n\n", message);
	fprintf(STDOUT, "Hint for Macintosh users:\n");
	fprintf(STDOUT, "Use the <Get Info> command of the Finder to increase the memory partition!\n\n");
#	if PARALLEL
		PP_Finalize();
#	endif
	exit(1);
} /* maerror */


/******************************************************
 * memory allocate double vectors, matrices, and cubes
 */

dvector new_dvector(int n)
{
	dvector v;

	v = (dvector) calloc((size_t) n, sizeof(double));
	if (v == NULL) maerror("step 1 in new_dvector");

	return v;
} /* new_dvector */


/******************/

dmatrix new_dmatrix(int nrow, int ncol)
{
	int i;
	dmatrix m;

	m = (dmatrix) calloc((size_t) nrow, sizeof(dvector));
	if (m == NULL) maerror("step 1 in in new_dmatrix");

	*m = (dvector) calloc((size_t) (nrow * ncol), sizeof(double));
	if (*m == NULL) maerror("step 2 in in new_dmatrix");

	for (i = 1; i < nrow; i++) m[i] = m[i-1] + ncol;

	return m;
} /* new_dmatrix */


/******************/

dcube new_dcube(int ntri, int nrow, int ncol)
{
	int i, j;
	dcube c;

	c = (dcube) calloc((size_t) ntri, sizeof(dmatrix));
	if (c == NULL) maerror("step 1 in in new_dcube");

	*c = (dmatrix) calloc((size_t) (ntri * nrow), sizeof(dvector));
	if (*c == NULL) maerror("step 2 in in new_dcube");

	**c = (dvector) calloc((size_t) (ntri * nrow * ncol), sizeof(double));
	if (**c == NULL) maerror("step 3 in in new_dcube");

	for (j = 1; j < nrow; j++) c[0][j] = c[0][j-1] + ncol;

	for (i = 1; i < ntri; i++) {
		c[i] = c[i-1] + nrow;
		c[i][0] = c[i-1][0] + nrow * ncol;
		for (j = 1; j < nrow; j++) c[i][j] = c[i][j-1] + ncol;
	}

	return c;
} /* new_dcube */


/******************/

void free_dvector(dvector v)
{
	free((double *) v);
} /* free_dvector */


/******************/

void free_dmatrix(dmatrix m)
{
	free((double *) *m);
	free((double *) m);
} /* free_dmatrix */


/******************/

void free_dcube(dcube c)
{
	free((double *) **c);
	free((double *) *c);
	free((double *) c);
} /* free_dcube */


/****************************************************
 * memory allocate char vectors, matrices, and cubes
 */

cvector new_cvector(int n)
{
	cvector v;

	v = (cvector) calloc((size_t) n, sizeof(char));
	if (v == NULL) maerror("step1 in new_cvector");

	return v;
} /* new_cvector */


/******************/

cmatrix new_cmatrix(int nrow, int ncol)
{
	int i;
	cmatrix m;

	m = (cmatrix) calloc((size_t) nrow, sizeof(cvector));
	if (m == NULL) maerror("step 1 in new_cmatrix");

	*m = (cvector) calloc((size_t) (nrow * ncol), sizeof(char));
	if (*m == NULL) maerror("step 2 in new_cmatrix");

	for (i = 1; i < nrow; i++) m[i] = m[i-1] + ncol;

	return m;
} /* new_cmatrix */


/******************/

ccube new_ccube(int ntri, int nrow, int ncol)
{
	int i, j;
	ccube c;

	c = (ccube) calloc((size_t) ntri, sizeof(cmatrix));
	if (c == NULL) maerror("step 1 in new_ccube");

	*c = (cmatrix) calloc((size_t) (ntri * nrow), sizeof(cvector));
	if (*c == NULL) maerror("step 2 in new_ccube");

	**c = (cvector) calloc((unsigned) (ntri * nrow * ncol), sizeof(char));
	if (**c == NULL) maerror("step 3 in new_ccube");

	for (j = 1; j < nrow; j++) c[0][j] = c[0][j-1] + ncol;

	for (i = 1; i < ntri; i++) {
		c[i] = c[i-1] + nrow;
		c[i][0] = c[i-1][0] + nrow * ncol;
		for (j = 1; j < nrow; j++) c[i][j] = c[i][j-1] + ncol;
	}

	return c;
} /* new_ccube */


/******************/

void free_cvector(cvector v)
{
	free((char *) v);
} /* free_cvector */


/******************/

void free_cmatrix(cmatrix m)
{
	free((char *) *m);
	free((char *) m);
} /* free_cmatrix */


/******************/

void free_ccube(ccube c)
{
	free((char *) **c);
	free((char *) *c);
	free((char *) c);
} /* free_ccube */


/***************************************************
 * memory allocate int vectors, matrices, and cubes
 */

ivector new_ivector(int n)
{
	ivector v;

	v = (ivector) calloc((size_t) n, sizeof(int));
	if (v == NULL) maerror("step 1 in new_ivector");

	return v;
} /* new_ivector */


/******************/

imatrix new_imatrix(int nrow, int ncol)
{
	int i;
	imatrix m;

	m = (imatrix) calloc((size_t) nrow, sizeof(ivector));
	if (m == NULL) maerror("step 1 in new_imatrix");

	*m = (ivector) calloc((size_t) (nrow * ncol), sizeof(int));
	if (*m == NULL) maerror("step 2 in new_imatrix");

	for (i = 1; i < nrow; i++) m[i] = m[i-1] + ncol;

	return m;
} /* new_imatrix */


/******************/

icube new_icube(int ntri, int nrow, int ncol)
{
	int i, j;
	icube c;

	c = (icube) calloc((size_t) ntri, sizeof(imatrix));
	if (c == NULL) maerror("step 1 in new_icube");

	*c = (imatrix) calloc((size_t) (ntri * nrow), sizeof(ivector));
	if (*c == NULL) maerror("step 2 in new_icube");

	**c = (ivector) calloc((size_t) (ntri * nrow * ncol), sizeof(int));
	if (**c == NULL) maerror("step 3 in new_icube");

	for (j = 1; j < nrow; j++) c[0][j] = c[0][j-1] + ncol;

	for (i = 1; i < ntri; i++) {
		c[i] = c[i-1] + nrow;
		c[i][0] = c[i-1][0] + nrow * ncol;
		for (j = 1; j < nrow; j++) c[i][j] = c[i][j-1] + ncol;
	}
	
	return c;
} /* new_icube */


/******************/

void free_ivector(ivector v)
{
	free((int *) v);
} /* free_ivector */


/******************/

void free_imatrix(imatrix m)
{
	free((int *) *m);
	free((int *) m);
} /* free_imatrix */


/******************/

void free_icube(icube c)
{
	free((int *) **c);
	free((int *) *c);
	free((int *) c);
} /* free_icube */


/***************************************************
 * memory allocate uli vectors, matrices, and cubes
 */

ulivector new_ulivector(int n)
{
	ulivector v;

	v = (ulivector) calloc((size_t) n, sizeof(uli));
	if (v == NULL) maerror("step 1 in new_ulivector");

	return v;
} /* new_ulivector */


/******************/

ulimatrix new_ulimatrix(int nrow, int ncol)
{
	int i;
	ulimatrix m;

	m = (ulimatrix) calloc((size_t) nrow, sizeof(ulivector));
	if (m == NULL) maerror("step 1 in new_ulimatrix");

	*m = (ulivector) calloc((size_t) (nrow * ncol), sizeof(uli));
	if (*m == NULL) maerror("step 2 in new_ulimatrix");

	for (i = 1; i < nrow; i++) m[i] = m[i-1] + ncol;

	return m;
} /* new_ulimatrix */


/******************/

ulicube new_ulicube(int ntri, int nrow, int ncol)
{
	int i, j;
	ulicube c;

	c = (ulicube) calloc((size_t) ntri, sizeof(ulimatrix));
	if (c == NULL) maerror("step 1 in new_ulicube");

	*c = (ulimatrix) calloc((size_t) (ntri * nrow), sizeof(ulivector));
	if (*c == NULL) maerror("step 2 in new_ulicube");

	**c = (ulivector) calloc((size_t) (ntri * nrow * ncol), sizeof(uli));
	if (**c == NULL) maerror("step 3 in new_ulicube");

	for (j = 1; j < nrow; j++) c[0][j] = c[0][j-1] + ncol;

	for (i = 1; i < ntri; i++) {
		c[i] = c[i-1] + nrow;
		c[i][0] = c[i-1][0] + nrow * ncol;
		for (j = 1; j < nrow; j++) c[i][j] = c[i][j-1] + ncol;
	}
	
	return c;
} /* new_ulicube */


/******************/

void free_ulivector(ulivector v)
{
	free((uli *) v);
} /* free_ulivector */


/******************/

void free_ulimatrix(ulimatrix m)
{
	free((uli *) *m);
	free((uli *) m);
} /* free_ulimatrix */


/******************/

void free_ulicube(ulicube c)
{
	free((uli *) **c);
	free((uli *) *c);
	free((uli *) c);
} /* free_ulicube */


/******************************************************************************/
/* check time                                                                 */
/******************************************************************************/
#define TIMECHECK_INTERVALL 300
/* check timer and generate message roughly every TIMECHECK_INTERVALL seconds */
void checktime(time_t *starttime, 	/* starttime of overall part   */
               time_t *lasttime,	/* lasttime of print out       */
               time_t *nowtime,		/* current time (taken inside) */
               uli     done,		/* number of tasks done        */
               uli     sumtodo,		/* sum of task todo            */
               int    *mflag)		/* LF needed to end line?      */
{
	double tc2, mintogo, minutes, hours;

	/* check timer */
	time(nowtime);
	if ( (*nowtime - *lasttime) > TIMECHECK_INTERVALL) {
		/* every TIMECHECK_INTERVAL seconds */
		/* percentage of completed tasks */
		if (*mflag == 0) {
			fprintf(STDOUT, "\n");
			*mflag = 1;
		}
		tc2 = 100.0 * done/sumtodo;
		mintogo = (100.0-tc2) *
			(double) (*nowtime-*starttime)/60.0/tc2;
		hours = floor(mintogo/60.0);
		minutes = mintogo - 60.0*hours;
		fprintf(STDOUT, "%2.2f%%", tc2);
		fprintf(STDOUT, " completed (remaining");
		fprintf(STDOUT, " time: %.0f", hours);
		fprintf(STDOUT, " hours %.0f", minutes);
		fprintf(STDOUT, " minutes)\n");
		fflush(STDOUT);
		*lasttime = *nowtime;
	} 
	/* else {fprintf(STDOUT,"t%ld,", *nowtime - *lasttime); fflush(STDOUT);} */

} /* checktime */


#ifndef SPRNG

/******************************************************************************/
/* random numbers generator  (Numerical recipes)                              */
/******************************************************************************/

/* variable */
long idum;

/* definitions */
#define IM1 2147483563
#define IM2 2147483399
#define AM (1.0/IM1)
#define IMM1 (IM1-1)
#define IA1 40014
#define IA2 40692
#define IQ1 53668
#define IQ2 52774
#define IR1 12211
#define IR2 3791
#define NTAB 32
#define NDIV (1+IMM1/NTAB)
#define EPS 1.2e-7
#define RNMX (1.0-EPS)

double randomunitintervall()
/* Long period (> 2e18) random number generator. Returns a uniform random
   deviate between 0.0 and 1.0 (exclusive of endpoint values).

   Source:
   Press et al., "Numerical recipes in C", Cambridge University Press, 1992
   (chapter 7 "Random numbers", ran2 random number generator) */
{
	int j;
	long k;
	static long idum2=123456789;
	static long iy=0;
	static long iv[NTAB];
	double temp;

	if (idum <= 0) {
		if (-(idum) < 1)
			idum=1;
		else
			idum=-(idum);
		idum2=(idum);
		for (j=NTAB+7;j>=0;j--) {
			k=(idum)/IQ1;
			idum=IA1*(idum-k*IQ1)-k*IR1;
			if (idum < 0)
				idum += IM1;
			if (j < NTAB)
				iv[j] = idum;
		}
		iy=iv[0];
	}
	k=(idum)/IQ1;
	idum=IA1*(idum-k*IQ1)-k*IR1;
	if (idum < 0)
		idum += IM1;
	k=idum2/IQ2;
	idum2=IA2*(idum2-k*IQ2)-k*IR2;
	if (idum2 < 0)
		idum2 += IM2;
	j=iy/NDIV;
	iy=iv[j]-idum2;
	iv[j] = idum;
	if (iy < 1)
		iy += IMM1;
	if ((temp=AM*iy) > RNMX)
		return RNMX;
	else
		return temp;
} /* randomunitintervall */

#undef IM1
#undef IM2
#undef AM
#undef IMM1
#undef IA1
#undef IA2
#undef IQ1
#undef IQ2
#undef IR1
#undef IR2
#undef NTAB
#undef NDIV
#undef EPS
#undef RNMX


int initrandom(int seed)   /* RAND4 */
{
   srand((unsigned) time(NULL));
   if (seed < 0) 
	seed = rand();
   idum=-(long) seed;
   fflush(STDOUT);
#  ifndef PARALLEL
	   fprintf(STDOUT,"Using RAND4 Random Number Generator\n");
#	   ifdef RANDVERBOSE1
	      fprintf(STDOUT, "!!! random seed set to %d !!!\n", seed);
#	   endif  /* RANDVERBOSE1 */
#  else /* PARALLEL */
	   {
	   int n;
	   if (PP_IamMaster) {
	     fprintf(STDOUT,"Using RAND4 Random Number Generator with leapfrog method\n");
	     fflush(STDOUT);
	   }
	   for (n=0; n<PP_Myid; n++)
		(void) randomunitintervall();
#	   ifdef RANDVERBOSE1
	      fprintf(STDOUT, "(%2d) !!! random seed set to %d, %dx drawn !!!\n", PP_Myid, seed, n);
#	   endif  /* RANDVERBOSE1 */
	   }
#  endif
   return (seed);
}  /* initrandom */ 

/******************/

#else /* USE_SPRNG */

/******************/

int *randstream;

int initrandom(int seed)
{
   srand((unsigned) time(NULL));
   if (seed < 0) 
	seed = make_sprng_seed();
#  ifndef PARALLEL
	   fprintf(STDOUT,"Using SPRNG -- Scalable Parallel Random Number Generator\n");
	   fflush(STDOUT);
	   randstream = init_sprng(0,1,seed,SPRNG_DEFAULT); /*init stream*/

#	   ifdef RANDVERBOSE1
	      fprintf(STDOUT, "!!! random seed set to %d !!!\n", seed);
	      fprintf(STDOUT, " Printing information about new stream\n");
	      print_sprng(randstream);
#	   endif /* RANDVERBOSE 1 */
#  else /* PARALLEL */
	   if (PP_IamMaster) {
	     fprintf(STDOUT,"Using SPRNG -- Scalable Parallel Random Number Generator\n");
	     fflush(STDOUT);
	   }
	   /* MPI_Bcast(&seed, 1, MPI_UNSIGNED, PP_MyMaster, MPI_COMM_WORLD); */
	   randstream = init_sprng(PP_Myid,PP_NumProcs,seed,SPRNG_DEFAULT); /*initialize stream*/
#	   ifdef RANDVERBOSE1
	      fprintf(STDOUT, "(%2d) !!! random seed set to %d !!!\n", PP_Myid, seed);
	      fprintf(STDOUT, " Printing information about new stream\n");
	      print_sprng(randstream);
#	   endif  /* RANDVERBOSE1 */
#  endif /* PARALLEL */
   return (seed);
}  /* initrandom */ 


#endif /* USE_SPRNG */

/******************/


/* returns a random integer in the range [0; n - 1] */
int randominteger(int n)
{
#  ifndef FIXEDINTRAND
#	ifndef PARALLEL
#	   ifdef SPRNG
		return (int) floor(sprng(randstream)*n);
#	   else /* NO_SPRNG */
		int t;
		t = (int) floor(randomunitintervall()*n);
		return t;
#	   endif /* NO_SPRNG */
#	else /* NOT PARALLEL */
#	   ifdef SPRNG
		return (int) floor(sprng(randstream)*n);
#	   else /* NO_SPRNG */
		int m;
		for (m=1; m<PP_NumProcs; m++)
			(void) randomunitintervall();
		PP_randn+=(m-1); PP_rand++;
		return (int) floor(randomunitintervall()*n);
#	   endif /* NO_SPRNG */
#	endif /* NOT PARALLEL */
#  else /* FIXEDINTRAND */
	fprintf(stderr, "!!! fixed \"random\" integers for testing purposes !!!\n");
	return (int)0;
#  endif /* FIXEDINTRAND */
} /* randominteger */


/******************/

/* Draw s numbers from the set 0,1,2,..,t-1 and put them
   into slist (every number can be drawn only one time) */
void chooser(int t, int s, ivector slist)
{
	int i;
	int randidx;
	ivector isfree;

	isfree = new_ivector(t);
	for (i = 0; i < t; i++) isfree[i] = i;

	for (i = t; i > t-s; i--) {
		/* random integer in the range [0, still to do = i-1] */
		randidx = randominteger(i);

		/* integer from array of free ints to slist */
		slist[t - i] = isfree[randidx];

		/* reduce list by moving last free to last chosen */
		isfree[randidx] = isfree[i-1];
	}
	free_ivector(isfree);
} /* chooser */



/******************/

/* Draw s numbers from the set 0,1,2,..,t-1 and put them
   into slist (every number can be drawn only one time) */
void fixedchooser(int t, int s, ivector slist)
{
	int i;

	for (i = 0; i < s; i++) {
		/* random integer in the range [0, s-1] */
		slist[i] = i;
	}
} /* fixedchooser */


/******************************************************************************/
/* Saver variants of ANSI C routines                                          */
/******************************************************************************/

/* a realloc function that works also on non-ANSI compilers */ 
void *myrealloc(void *p, size_t s)
{
	/* void *tmpptr; */  /* for debugging realloc - done ;^) HAS */
	if (p == NULL) {
		/* tmpptr = malloc(s); */  /* for debugging realloc - done ;^) HAS */
		return malloc(s);
	} else {
		/* tmpptr = realloc(p, s); */  /* for debugging realloc - done ;^) HAS */
		return realloc(p, s);
	}
	/* return tmpptr; */  /* for debugging realloc - done ;^) HAS */
} /* myrealloc */


/******************/

/* safer variant of gets */
/* Reads characters from stdin until a newline character or EOF
   is received.  The newline is not made part of the string.
   If an error occurs a null string \0 is returned */
cvector mygets()
{
	int c, n;
	cvector str;

	str = new_cvector(100);
	
	n = 0;
	c = getchar();
	while ((c != '\n') && (c != '\r') 
               && (n < 99) && (c != EOF) 
               && !ferror(stdin))
	{
		str[n] = (char) c;
		c = getchar();
		n++;
	}
	if (c == EOF || ferror(stdin))
	{
		str[0] = '\0';
	}
	else
	{
		str[n] = '\0';
	}
	
	return str;
} /* mygets */



/******************/

/* safer variant of gets */
/* Reads characters from infp until a newline character or EOF
   is received.  The newline is not made part of the string.
   If an error occurs a null string \0 is returned */
cvector myfgets(FILE *infp)
{
	int c, n;
	cvector str;

	str = new_cvector(100);
	
	n = 0;
	c = getc(infp);
	/* c = getchar(); */
	while ((c != '\n') && (c != '\r') 
               && (n < 99) && (c != EOF) 
               && !ferror(infp))
	{
		str[n] = (char) c;
		c = getc(infp);
		/* c = getchar(); */
		n++;
	}
	if (c == EOF || ferror(infp))
	{
		str[0] = '\0';
	}
	else
	{
		str[n] = '\0';
	}
	
	return str;
} /* myfgets */



/******************************************************************************/
/* minimization of a function by Brents method (Numerical Recipes)            */
/******************************************************************************/

double brent(double, double, double, double (*f )(double ), double, double *, double *, double, double, double, int *);


#define ITMAX 100
#define CGOLD 0.3819660
#define GOLD 1.618034
#define GLIMIT 100.0
#define TINY 1.0e-20
#define ZEPS 1.0e-10
#define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))

/* Brents method in one dimension */
double brent(
	double ax, 		/* three x-values ax < bx < cx */
	double bx, 		/* or             ax > bx > cx */
	double cx, 
	double (*f)(double), 	/* function to minimize, f(bx)=min(f(ax),f(bx),f(cx)) */
	double tol,		/* describes the precision */
	double *foptx, 
	double *f2optx, 
	double fax, 		/* f(ax), already precomputed outside of brent() */
	double fbx, 		/* f(bx), already precomputed outside of brent() */
	double fcx,		/* f(cx), already precomputed outside of brent() */
	int    *converged)	/* not converged error flag */
{
	int iter;
	double a,b,d=0,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm;
	double xw,wv,vx;
	double e=0.0;

	*converged=TRUE;

	a=(ax < cx ? ax : cx);	/* is the order correct? If not, exchange ax and cx */
	b=(ax > cx ? ax : cx);

	x=bx;			/* store 'middle' x, and y=fx */
	fx=fbx;

	if (fax < fcx) {
		w=ax;
		fw=fax;
		v=cx;
		fv=fcx;
	} else {
		w=cx;
		fw=fcx;
		v=ax;
		fv=fax;	
	}

	for (iter=1;iter<=ITMAX;iter++) {		/* iteration loop */
		xm=0.5*(a+b);
		tol2=2.0*(tol1=tol*fabs(x)+ZEPS);
		/* is precision OK? I.e., are a and b close enough. */
		if (fabs(x-xm) <= (tol2-0.5*(b-a))) { /* yes -> finish */
			*foptx = fx;		/* return current f(x) */
			if ((x==w) && (x==v)) {  
				/* to avoid FPE errors by zero-division */
				v=x-ZEPS;
				w=x+ZEPS;
				fv=(*f)(v);
				fw=(*f)(w);
			}
			xw = x-w;
			wv = w-v;
			vx = v-x;
			*f2optx = 2.0*(fv*xw + fx*wv + fw*vx)/
				(v*v*xw + x*x*wv + w*w*vx);	/* return S.E. of f(x) */
#if 0
fprintf(stderr, "XXXX: x=%f f(x)=%f=%f S.E.=%f   w=%f v=%f\n", x, *foptx, fx, *f2optx, w, v);
#endif
			return x;	/* return minimum x-value */
		}
		if (fabs(e) > tol1) {		/* compute a parabolic fit */
			r=(x-w)*(fx-fv);
			q=(x-v)*(fx-fw);
			p=(x-v)*q-(x-w)*r;
			q=2.0*(q-r);
			if (q > 0.0) p = -p;
			q=fabs(q);
			etemp=e;
			e=d;
			/* determine acceptability of the parabolic fit */
			if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x))
				/* compute golden section of the larger segment max(a-x,b-x) */
				d=CGOLD*(e=(x >= xm ? a-x : b-x));
			else {
				/* accept the parabolic step */
				d=p/q;
				u=x+d;
				if (u-a < tol2 || b-u < tol2)
					d=SIGN(tol1,xm-x);
			}
		} else {
			/* compute golden section of the larger segment max(a-x,b-x) */
			d=CGOLD*(e=(x >= xm ? a-x : b-x));
		}
		u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d));
		fu=(*f)(u);

		/* new u better than old x ? */
		if (fu <= fx) { /* yes -> accept it */
			if (u >= x) a=x; else b=x;
			SHFT(v,w,x,u)		/* ...and use the new values */
			SHFT(fv,fw,fx,fu)
		} else {
			if (u < x) a=u; else b=u;
			if (fu <= fw || w == x) {
				v=w;
				w=u;
				fv=fw;
				fw=fu;
			} else if (fu <= fv || v == x || v == w) {
				v=u;
				fv=fu;
			}
		}
	} /* end loop */

	/* the function should never get here, i.e., the search did not converge within ITMAX iterations */
	*converged=FALSE;

	*foptx = fx;	/* return f(x) */
	if ((x==w) && (x==v)) {  /* regularize to avoid FPE errors by zero-division */
		v=x-ZEPS;
		w=x+ZEPS;
		fv=(*f)(v);
		fw=(*f)(w);
	}
	xw = x-w;
	wv = w-v;
	vx = v-x;
	*f2optx = 2.0*(fv*xw + fx*wv + fw*vx)/
		(v*v*xw + x*x*wv + w*w*vx);	/* return S.E. of f(x) */
#if 0
fprintf(stderr, "YYYY: x=%f f(x)=%f=%f S.E.=%f   w=%f v=%f\n", x, *foptx, fx, *f2optx, w, v);
#endif
	return x;	/* return minimum x-value */
} /* brent */
#undef ITMAX
#undef CGOLD
#undef ZEPS
#undef SHFT
#undef SIGN
#undef GOLD
#undef GLIMIT
#undef TINY


/******************/

/* one-dimensional minimization - as input a lower and an upper limit and a trial
  value for the minimum is needed: xmin < xguess < xmax
  the function and a fractional tolerance has to be specified
  onedimenmin returns the optimal x value and the value of the function
  and its second derivative at this point
  */
double onedimenmin(
	double xmin, double xguess, double xmax, double (*f)(double),
	double tol, double *fx, double *f2x)
{
	double eps, optx, ax, bx, cx, fa, fb, fc;
	int    converged;	/* not converged error flag */
		
	/* first attempt to bracketize minimum */
	eps = xguess*tol*50.0;
	ax = xguess - eps;
	if (ax < xmin) ax = xmin;
	bx = xguess;
	cx = xguess + eps;
	if (cx > xmax) cx = xmax;
	
	/* check if this works */
	fa = (*f)(ax);
	fb = (*f)(bx);
	fc = (*f)(cx);

	/* if it works use these borders else be conservative */
	if ((fa < fb) || (fc < fb)) {
		if (ax != xmin) fa = (*f)(xmin);
		if (cx != xmax) fc = (*f)(xmax);
		optx = brent(xmin, xguess, xmax, f, tol, fx, f2x, fa, fb, fc, &converged);
	} else
		optx = brent(ax, bx, cx, f, tol, fx, f2x, fa, fb, fc, &converged);

	return optx; /* return optimal x */
} /* onedimenmin */


/******************/

/* two-dimensional minimization with borders and calculations of standard errors */
/* we optimize along basis vectors - not very optimal but it seems to work well */
void twodimenmin(double tol,
	int active1, double min1, double *x1, double max1, double (*func1)(double), double *err1,
	int active2, double min2, double *x2, double max2, double (*func2)(double), double *err2)
{
	int it, nump, change;
	double x1old, x2old;
	double fx, f2x;

	it = 0;
	nump = 0;

	/* count number of parameters */
	if (active1) nump++;
	if (active2) nump++;

	do { /* repeat until nothing changes any more */
		it++;
		change = FALSE;

		/* optimize first variable */
		if (active1) {

			if ((*x1) <= min1) (*x1) = min1 + 0.2*(max1-min1);
			if ((*x1) >= max1) (*x1) = max1 - 0.2*(max1-min1);
			x1old = (*x1);
			(*x1) = onedimenmin(min1, (*x1), max1, func1, tol, &fx, &f2x);
			if ((*x1) < min1) (*x1) = min1;
			if ((*x1) > max1) (*x1) = max1;
			/* same tolerance as 1D minimization */
			if (fabs((*x1) - x1old) > 3.3*tol) change = TRUE;
			
			/* standard error */
			f2x = fabs(f2x);
			if (1.0/(max1*max1) < f2x) (*err1) = sqrt(1.0/f2x);
			else (*err1) = max1;

		}

		/* optimize second variable */
		if (active2) {

			if ((*x2) <= min2) (*x2) = min2 + 0.2*(max2-min2);
			if ((*x2) >= max2) (*x2) = max2 - 0.2*(max2-min2);
			x2old = (*x2);
			(*x2) = onedimenmin(min2, (*x2), max2, func2, tol, &fx, &f2x);
			if ((*x2) < min2) (*x2) = min2;
			if ((*x2) > max2) (*x2) = max2;
			/* same tolerance as 1D minimization */
			if (fabs((*x2) - x2old) > 3.3*tol) change = TRUE;
			
			/* standard error */
			f2x = fabs(f2x);
			if (1.0/(max2*max2) < f2x) (*err2) = sqrt(1.0/f2x);
			else (*err2) = max2;

		}

		if (nump == 1) return;
		
	} while (it != MAXITS && change);

	return;
} /* twodimenmin */

