#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <float.h>
#include "gpkieee754.h"
#include "gpk_std.h"
#include "numerics.h"
#include "gpklib.h"
#include "gpkerr.h"
#include "innerloop2.h"
#include "m22/v2.h"
#include "m22/m22.h"
#include "m22/m22v2.h"
#include "nfold_vec.h"

#ifdef _REENTRANT
#include <thread.h>
#include <signal.h>
#endif

LOCAL C int num_for_decr = 6;
LOCAL C double exploredFac = 2.0;
LOCAL C double exploredFac_notrust = 0.10;
LOCAL C double SpreadFac = 0.6;
LOCAL C double SpreadDecr = 2.0;
LOCAL C int AFew = 5;

inline double xx(double x) {return x*x;}

inline double max(double a, double b)	{ return a>b  ? a : b;}
inline double min(double a, double b)	{ return a<b  ? a : b;}
inline int imin(int a, int b)	{ return a<b  ? a : b;}
static double max(double a, double b, double c)	{ return max(a, max(b, c));}

static double minmax(double mn, double x, double mx)
{ return max(mn, min(x, mx)); }

#define D if(0)
#define DD if(0)

C BOOL DEBUG_DIRSET = 0;

void boom(int);
#define BOOM	boom(__LINE__+30000)


#ifdef _REENTRANT

unsigned intrand(int r)
{
 static mutex_t m;
 
 mutex_lock(&m);
 double tmp = ran1(0);
 mutex_unlock(&m);

 return (int)(r*tmp);
}

#else

unsigned intrand(int r)
{
 double tmp = ran1(0);

 return (int)(r*tmp);
}

#endif


double unirand()
{
 double t = intrand(1048576)/1048576.0;
 t += intrand(1048576)/1e9;
 if(t >= 1)
	t -= 1;
 return t;
}


LOCAL double logrand()
{
 C double rv =  -log(unirand());
 return rv;
}


LOCAL double normalrand()
{
 C static double qn = quiet_nan(0);
 static double extra = qn;
 if(!isnan(extra))	{
	C double tmp = extra;
	extra = qn;
	return tmp;
	}

 C double t1 = sqrt(-2*log(unirand()));
 C double angle = 2*M_PI*unirand();
 extra = t1 * sin(angle);
 return t1 * cos(angle);
}




LOCAL void copya(double *to, C double *from, int n)
{
 memcpy(to, from, n*sizeof(double));
}


LOCAL double dot(C double *x, C double *y, int n)
{
 double dotsum = 0;
 BOOM;
 INNERLOOP(i, 0, n, dotsum += x[i]*y[i]);
 return dotsum;
}





vector2 scale(C vector2& a, C vector2& b) { return vector2(a(0)/b(0), a(1)/b(1)); }
// BOOL operator==(C vec& a, C vec& b) { return a.x0==b.x0 && a.x1==b.x1; }
// BOOL operator!=(C vec& a, C vec& b) { return a.x0!=b.x0 || a.x1!=b.x1; }



vector2 perp_to(C vector2& a)	{ return vector2(a(1), -a(0)); }
double hypot(C vector2& a) { return a.norm();}




LOCAL double *calc_combo(C double *d1, C double *d2, int n, C vector2& x)
{
 assert(finite(x));
 double *tmp = double1alloc(n);
 assert(tmp);

 INNERLOOP(i, 0, n, tmp[i] = d1[i]*x(0) + d2[i]*x(1));

 boom(302);
 return tmp;
}


double * calc_combo(C double *pc, C double *d1, C double *d2, int n, C vector2& x)
{
 double *tmp = double1alloc(n);
 assert(tmp);
 assert(finite(x));

 INNERLOOP(i, 0, n, tmp[i] = pc[i] + d1[i]*x(0) + d2[i]*x(1));

 boom(302);
 return tmp;
}



void print_pc(C double *pc, int n, FILE *fp, C char *prefix = 0)
{
 boom(2000);
 if(fp)	{
	int i;
	if(prefix)
		fprintf(fp, "%s", prefix);
	for(i=0;i<n;i++)
		fprintf(fp, "%g%c", pc[i], i==n-1 ? '\n' : ' ');
	fflush(fp);
	}
}


LOCAL double hypot(C double *x, int n)
{
 double s = 0;
 INNERLOOP(i, 0, n, s += xx(x[i]));
 return sqrt(s);
}


void summarize_dir(C char *prefix, FILE *fp, C double *pi, C double *sz, int n, int nshow)
{
 int j;
 int nn = imin(n, nshow);
 int *tmp = int1alloc(nn);
 assert(tmp);
 for(j=0; j<nn; j++)
	tmp[j] = -1;

 for(j=0; j<n; j++)	{
	int k;
	for(k=nn-1; k>=0  &&  (tmp[k]==-1 || fabs(pi[j])>fabs(pi[tmp[k]])); k--)	
			;
	int m = k+1;
	for(k=nn-2; k>=m; k--)
		tmp[k+1] = tmp[k];
	if(m < nn)
		tmp[m] = j;
	}
 fprintf(fp, "%s", prefix);
 for(j=0; j<nn && pi[tmp[j]]!=0; j++)	{
	if(sz)
		fprintf(fp, "p%d= %g*%g ", tmp[j], pi[tmp[j]], sz[tmp[j]]);
	else
		fprintf(fp, "p%d= %g*sz ", tmp[j], pi[tmp[j]]);
	}
 if(fabs(hypot(pi, n)-1) > 0.0001)
	fprintf(fp, j<n ? "...(hypot=%g)\n" : "(hypot=%g)\n",
			hypot(pi, n));
 else
	fprintf(fp, j<n ? "...\n" : "\n");
 int1free(tmp);
}



LOCAL BOOL orthonormalize(double *pdi, C double *p_last, int n)
{
 assert(pdi != p_last);
 if(!(fabs(hypot(pdi, n) - 1) < 0.001))	{
	fprintf(stderr, "INFO: hypot(pdi)=%g\n", hypot(pdi, n));
	summarize_dir("INFO: pdi is:", stderr, pdi, 0, n, 10);
	return FALSE;
	}
 assert(fabs(hypot(pdi, n) - 1) < 0.001);
 assert(fabs(hypot(p_last, n) - 1) < 0.001);
 C double fac = dot(pdi, p_last, n)/dot(p_last, p_last, n);
 BOOM;
 assert( finite(fac));

 /* Subtract out part of pdi in direction of p_last. */
 INNERLOOP(i, 0, n, pdi[i] -= fac*p_last[i]);

 C double pd = hypot(pdi, n);
 INNERLOOP(i, 0, n, pdi[i] /= pd);
 D if(pd < 0.99)
	fprintf(stderr, "#Fac for orthogonalization=%g = length_ratio=%g\n",
			fac, sqrt(pd));

 if(!(pd > 0.001))	{
	D fprintf(stderr,
		"# loss of orthogonality: pd=%g, fac=%g\n",
		pd, fac);
	print_pc(p_last, n, stderr, "#p_last: ");
	return FALSE;
	}

 return TRUE;
}



#define MAXPARAM	6

LOCAL BOOL
invert(double ainv[MAXPARAM][MAXPARAM], int Np)		/* Invert matrix */
{
	int kx,im[MAXPARAM],jm[MAXPARAM];
	double bx[MAXPARAM],c[MAXPARAM];
	C double eps = 1e-31;


boom(100);
/*
* zero out the indices marking the pos of the max for each param
* (finding the max helps precision of final ans)
*/
	{
	int j;
	for(j=0; j<Np; ++j)
		im[j] = jm[j] = 0;
	}

/* find location of max for each param- here called pivot */
	for(kx=0; kx<Np; ++kx) {
		/*determination of pivot element*/
		double pivot = 0;

		/* fprintf(stderr, "Gjr1.%d\n", kx); */
		/* start at 'kx' bec only have to do param's once */
		{
		int i;
		double fabs_pivot = 0;
		for(i=kx; i<Np; ++i) {
			int j;

			for(j=kx; j < Np; ++j) {
				if(fabs(ainv[i][j]) > fabs_pivot) {
					pivot = ainv[i][j];
					fabs_pivot = fabs(pivot);
					jm[kx] = i;
					im[kx] = j;
					}
				}
			}
		}

		/* if max val is tiny,  something's wrong */
		if(fabs(pivot) < eps) {
			fprintf(stderr, "Singular Matrix, kx=%d, im,jm=%d,%d:\n",
									kx, im[kx], jm[kx]);
			int iq;
			for(iq=0;iq<Np;iq++)	{
				int jq;
				for(jq=0;jq<Np;jq++)
					fprintf(stderr, "%g ", ainv[iq][jq]);
				fprintf(stderr, "\n");
				}

			return FALSE;
			}

		/* if max is not on diag, move it there */
		if(jm[kx] != kx)	{	/* row swap */
			int j;

			for(j=0; j< Np; ++j) {
				C int l = jm[kx];
				C double z = ainv[l][j];

				ainv[l][j] = ainv[kx][j];
				ainv[kx][j] = z;
				}
			}

		if(im[kx] != kx)	{	/* column swap*/
			int ii;

			for(ii=0; ii<Np; ++ii) {
				C int l = im[kx];
				C double z = ainv[ii][l];

				ainv[ii][l] = ainv[ii][kx];
				ainv[ii][kx] = z;
				}
			}

		{
		int i;
		/*Gauss-Jordan method to manip matrix I/A to get invA */
		for(i=0; i<Np; ++i) {
			if(i == kx)	{	/* diag elem must be div by self2*/
				bx[i] = 1./ pivot;
				c[i] = 1.;
				}
			else	{	/* others by diag, then frac made up
						* of col elems of the 'kx' col(saved as bx)
						*sub'd from self (saved in 'c'*/
				bx[i] = (-ainv[kx][i])/pivot;
				c[i] = ainv[i][kx];
				}
			ainv[kx][i] = ainv[i][kx] = 0.;
			}

		/* fprintf(stderr, "Gjr6.%d\n", kx); */
		for(i=0; i<Np; ++i)	{
			int j;

			for(j=0; j<Np; ++j)
				ainv[i][j] += c[i]*bx[j];
			}
		}
	}

	/*reordering the matrix - undo the swap for max */
	{
	int m;
	/* fprintf(stderr, "Gjr7\n"); */
	for(m=0; m<Np; ++m) {
		kx = Np-m -1;	
		/* fprintf(stderr, "Gjr7.%d\n", kx); */
		if(jm[kx] != kx) {
			int i;

			for(i=0; i<Np; ++i) {
				C int l = jm[kx];
				C double z=ainv[i][l];

				ainv[i][l] = ainv[i][kx];
				ainv[i][kx] = z;
				}
			}
		if(im[kx] != kx) {
			int j;
			for(j=0; j<Np; ++j) {
				C int l = im[kx];
				C double z = ainv[l][j];

				ainv[l][j] = ainv[kx][j];
				ainv[kx][j] = z;
				}
			}
		}
	}
 return TRUE;
}



class stuff	{
	int magic;
	public:
	vector2 v;
	double y;
	stuff() : v(quiet_nan(0), quiet_nan(0)), y(quiet_nan(0)) {;}
	stuff(double xx0, double xx1, double yy)
	: v(xx0, xx1), y(yy) {;}
	stuff(C vector2& x, double yy) : v(x), y(yy) {;}
	operator int() C	{return finite(v) && !isnan(y);}
	int operator !() C	{return !finite(v) || isnan(y);}
	};


typedef C stuff *Cstuffp;
class sortedstuff	{
	stuff *p;
	int nn, nalloc;
	double T;

	public:

	sortedstuff(int n_alloc, double T_)
		: p(new stuff[n_alloc]), nn(0), nalloc(n_alloc), T(T_) { assert(p);}
	~sortedstuff()	{delete [] p;}
	stuff add(C stuff& tmp);
	void add(C sortedstuff& x);
	int n() C	{return nn;}
	operator Cstuffp() C	{return p;}
	stuff operator[](int i) C {return p[i];}
	};


stuff sortedstuff::add(C stuff& tmp)
{			/* Crude insertion sort, with energies uncertain by T. */
 int i;
 boom(200);
 if(isnan(tmp.y))
	return tmp;

 for(i=0; i<nn; i++)
	if(hypot(tmp.v - p[i].v) < 1e-6)
		return p[i];	/* Already entered. */

 C double tmpy = tmp.y + 0.5*T*normalrand();
 assert(nalloc>0 && nn>=0);
 if(nn >= nalloc)
	nn--;

 for(i=0;i<nn && !(p[i].y>=tmpy); i++)
	;
 {
 int j;
 for(j=nn-1;j>=i;j--)
	p[j+1] = p[j];
 }
 nn++;
 p[i] = tmp;
 return p[i];
}
 

void sortedstuff::add(C sortedstuff& x)
{
 int i;
 for(i=0; i<x.n(); i++)
	add(x[i]);
}


struct min_xf {
	double est_min;
	double xf;
	min_xf(double ssa, double xxf) : est_min(ssa), xf(xxf) {;}
	};


LOCAL  stuff Try(double (*funk)(C double *, int, void *, int, int, int),
					void *c, int nc, int copy, int iter,
					C double *pc, C double *d0, C double *d1, int n,
					C vector2& x)
{
 assert(finite(x));
 double *tmp = calc_combo(pc, d0, d1, n, x);
 boom(400);
 C double ytmp = (*funk)(tmp, n, c, nc, copy, iter);
 D fprintf(stderr, "\t[copy=%1d]Try(%g,%g){pc=%g... tmp=%g...} = %g\n",
					copy, x(0), x(1), pc[0], tmp[0], ytmp);
 double1free(tmp);
 return stuff(x, ytmp);
}



LOCAL BOOL lowest_point_on_edge(C sortedstuff &s)
{
 int i;
 for(i=2; i<s.n(); i++)	{
	int j;
	for(j=1; j<i; j++)
		if(dot(s[i].v-s[0].v, s[j].v-s[0].v) < 0)
			return FALSE;	/* There is a point opposite to s[i].v */
	}

 /* There is some point which doesn't
	have an opposite.  Therefore, s[0]
	must be on the edge of the distribution. */
 return TRUE;
}



LOCAL double estimate_min(C sortedstuff& s, C vector2& expdir, C vector2& startpt)
{
 return dot(expdir, (s[0].v-startpt)*3
			+(s[1].v-startpt)*2
			+(s[2].v-startpt)*1)
			/(expdir.norm() * (3+2+1));
}



#define LOTS	100

/* expdir sets the exploration direction, startpt sets the starting point. */
LOCAL min_xf explore_inner(sortedstuff *ss,
				double (*funk)(C double *, int, void *, int, int, int),
				void *c, int nc, int copy, int iter,
				C double *pc , C double *d0, C double *d1, int n,
				double dely, double T,
				C vector2& expdir, C stuff& startpt)
{
 assert(finite(expdir));
 assert(finite(startpt));
 boom(500);
 double xf = 1;
 sortedstuff s(3+LOTS, T);
 if(finite(startpt.y))
	s.add(startpt);

 double y1 = s.add(Try(funk, c, nc, copy, iter,
							pc, d0, d1, n, expdir*xf+startpt.v)).y;
 boom(600);
 while(y1-s[0].y>2*dely && fabs(xf)>0.03)	{
	xf /= -2;
	y1 = s.add(Try(funk, c, nc, copy, iter, pc, d0, d1, n, expdir*xf+startpt.v)).y;
	}
 while(xf>=1 && xf<20 && y1-s[0].y<0.5*dely)	{
	C double os0y = s[0].y;
	xf *= 2;
	y1 = s.add(Try(funk, c, nc, copy, iter, pc, d0, d1, n, expdir*xf+startpt.v)).y;
	if(y1 > os0y)
		xf *= -1;
	}

 boom(700);
 if(s.n()<3 || !finite(s[2]))	{
	if(y1-s[0].y>dely || xf<1)
		s.add(Try(funk, c, nc, copy, iter,
								pc, d0, d1, n, expdir*(-0.6*xf)+startpt.v));
	else
		s.add(Try(funk, c, nc, copy, iter,
								pc, d0, d1, n, expdir*(1.7*xf)+startpt.v));
	}

 if(s.n()>1 && lowest_point_on_edge(s))	{
	fprintf(stderr, "\t[copy=%d] lowest point at end in explore_inner(): extend.\n", copy);
	s.add(Try(funk, c, nc, copy, iter, pc, d0, d1, n, s[0].v*2.0-s[1].v));
	}

 if(s.n()==2 || !finite(s[2]))	{
	fprintf(stderr, "\t[copy=%d] having trouble finding enough finite data: explore_inner()\n", copy);
	s.add(Try(funk, c, nc, copy, iter, pc, d0, d1, n, (s[0].v+s[1].v)*0.5));
	}

 ss->add(s);

 return min_xf(estimate_min(s, expdir, startpt.v), xf);
}




LOCAL double go_accept_step(C sortedstuff& s, double *pc,
							C double *d0, C double *d1, int n,
							double T)
{
 if(s.n()<2 || !finite(s[0].y))
	return quiet_nan(0);

 C double zero = s[0].y - 0.5*(finite(s[1].y) ? 0.5*(s[1].y-s[0].y) : 0)
						- 0.5*T/s.n();
 C double E = zero + T*logrand();
 int chosen = -1;
 {
 double diff = infinity();
 int i;
 for(i=0; i<s.n(); i++)	{
	/* We probably take the measurement that has its energy
	   closest to E.  Measurements that differ by only T/s.n() are
	   considered interchangeable. */

	C double tmp = fabs(s[i].y - E) - (T/s.n())*logrand();
	if(tmp < diff)	{
		diff = tmp;
		chosen = i;
		}
	}
 }
 assert(chosen >= 0);
 assert(finite(s[chosen].v));

 D fprintf(stderr, "\tgo_accept_step [%d] (%g,%g)=%g, target E=%g, zero=%g, T=%g\n",
					chosen, s[chosen].v(0), s[chosen].v(1), s[chosen].y, E, zero, T);
 
 double *tmp = calc_combo(pc, d0, d1, n, s[chosen].v);
 copya(pc, tmp, n);
 double1free(tmp);
 return s[chosen].y;
}




struct vecpair	{
	vector2 v0, v1;
	vecpair(C vector2& a, C vector2& b) : v0(a), v1(b) {;}
	vecpair() : v0(), v1() {;}
	};


LOCAL BOOL finite(C vecpair & x)
{ return finite(x.v0) && finite(x.v1); }


LOCAL double sign(double x) { return x>0 ? 1 : (x<0 ? -1 : 0); }


/* See Numerical Recipies. */
LOCAL void jacobi_step(C matrix22& c, vector2 *ev1, vector2 *ev2)
{
 C double theta = (c(1,1)-c(0,0))/(c(0,1)+c(1,0));
 if(!finite(theta) || theta>1e10)	{
	*ev1 = vector2(1.0, 0.0);
	*ev2 = vector2(0.0, 1.0);
	}
 C double cc = 1 / sqrt(theta*theta + 1);
 C double t = (theta>0 ? 1 : -1) / (fabs(theta) + sqrt(theta*theta + 1));
 *ev1 = vector2(cc, -t*cc);  /* reverse? */
 *ev2 = vector2(t*cc, cc);
}



int rank_r(C sortedstuff& s, int which, C vector2& ctr)
{
 int i, rank = 0;
 assert(s.n() > 0);
 C double r = hypot(s[which].v - ctr);
 BOOM;
 for(i=0; i<s.n(); i++)
	if(hypot(s[i].v-ctr) < r)
		rank++;
 return rank;
}


int rank_y(C sortedstuff& s, int which)
{
 int i, rank = 0;
 assert(s.n() > 0);
 C double y = s[which].y;
 BOOM;
 for(i=0; i<s.n(); i++)
	if(s[i].y < y)
		rank++;
 return rank;
}



LOCAL double move_toward_axis(double num, double cf)
{
 int sign = num>0 ? 1 : -1;
 double tgt = fabs(num)>sqrt(0.5) ? sign : 0;
 return cf*num + (1-cf)*tgt;
}


LOCAL vector2 move_toward_axes(vector2 a, double cf)
{
 fprintf(stderr, "move_toward_axes: cf=%g\n", cf);
 assert(cf>=0 && cf<=1);
 a = normalize(a);
 assert(fabs(hypot(a) - 1) < 0.1);
 vector2 tmp(move_toward_axis(a(0), cf), move_toward_axis(a(1), cf));
 return normalize(tmp);
}



LOCAL vecpair get_nonparab_dirs(C sortedstuff& s, double dely, vector2 *maybe_dir = 0)
{
 boom(1100);
 vector2 sum1(0.0, 0.001), sum2(0.0, 0.001), sump(0.0, 0.0);
 N2foldVec up_dir(0.0, 0.001);
 double sw1 = 0.01, sw2 = 0.01;
 int i, saw = 0;

 if(s.n() <= 2)
	return vecpair();

 /* Accumulate info on steep direction: */
 for(i=1; i<s.n(); i++)	{
	C int w = rank_y(s, i) - rank_r(s, i, s[0].v);
	C double ew = exp(w);
	C vector2 n = normalize(s[i].v - s[0].v);
	assert(finite(n));
	sum1 += n * ew;
	sum2 += n * (1/ew);
	sump += n;	 /* Find out where most exploration was: */
	up_dir += N2foldVec(n) * w;
	saw += abs(w);
	sw1 += ew;
	sw2 += 1/ew;
	}


 /* Point away from worst points: */
 vector2 updir(up_dir.pos(0));
 /* Aim the steep direction as much as possible away
	from high points, toward low points, and away from points in general: */
 if(dot(updir, sum1)/sw1
		- dot(updir, sum2)/sw2
		+ dot(updir, sump)/(s.n()-1) > 0)
	updir *= -1;
 assert(finite(updir));
 assert(hypot(updir) > 0.00000001);

 C double confidence = saw/(double)(saw + s.n());
 /* If you're not confident in the directions, leave them close to the axes: */
 fprintf(stderr, "hypot(updir)[%g,%g] = %g\n", updir(0), updir(1), hypot(updir));
 updir = move_toward_axes(updir, confidence);
 assert(finite(updir));

 vector2 shallow = perp_to(updir);
 /* Aim the shallow direction as much as possible away
	from high points, toward low points, and away from points in general: */
 if(dot(shallow, sum1)/sw1
		- dot(shallow, sum2)/sw2
		+ dot(shallow, sump)/(s.n()-1) > 0)
	shallow *= -1;

 if(maybe_dir)
 	*maybe_dir = sum1 * (-1/sw1);

 {
 /* Now, find out a typical distance in the steep direction:
	how far can you go and still be at low altitude. */
 double sm = 0, smw = 0;
 for(i=1;i<s.n();i++)
	if(hypot(s[i].v-s[0].v)>1e-4 && (i==1 || finite(s[i].y)))	{
		double f = sqrt(dely/fabs(s[i].y-s[0].y));
		if(!(f<4))	f = 4;
		if(!(f>0.25))	f = 0.25;
			/* f is sort of an extrapolant to estimate how far you'd
				go until things increase by dely. */
		C double dotp = dot(updir, s[i].v-s[0].v);
		C double w = exp(fabs(dotp)/hypot(s[i].v-s[0].v));
			/* w is a weight; it's big for points nearly along updir. */
		sm += fabs(dotp)*f * w;
		smw += w;
		}
 updir *= smw!=0 ? sm/smw : 0.01;
 }

 {
 /* Now, find out a typical distance in the shallow direction:
	how far can you go and still be at low altitude. */
/* We emphasize the longest distance, relative to the calculation for the
	steep direction. */
 double sm = 0, smw = 0;
 for(i=1;i<s.n();i++)
	if(hypot(s[i].v-s[0].v)>1e-4 && (i==1 || finite(s[i].y)))	{
		double f = sqrt(dely/fabs(s[i].y-s[0].y));
		if(!(f<10))	f = 10;
		if(!(f>0.1))	f = 0.1; /* f is sort of an extrapolant to
									estimate how far you'd
									go until things increase by dely. */
		C double dotp = dot(shallow, s[i].v-s[0].v);
		C double w = exp(2*fabs(dotp)/hypot(s[i].v-s[0].v));
			/* w is a weight; it's big for points nearly along shallow. */
		sm += xx(dotp*f) * w;
		smw += w;
		}
 shallow *= smw!=0 ? sqrt(sm/smw) : 0.1;
 }
 BOOM;

 D fprintf(stderr, "\tnonparabolic up direction=(%f,%f), shallow=(%g,%g)\n",
				updir(0), updir(1), shallow(0), shallow(1));

 /* Swap, if necessary so that directions are rotated less than pi/4: */
 if(fabs(atan(shallow(1)/shallow(0))) > M_PI/4)	{
	C vector2 tmp = shallow;
	shallow = updir;
	updir = tmp;
	}
 return vecpair(shallow, updir);
}


LOCAL int in_line2(C vector2& a, C vector2& b, C vector2& c)
{	/* Is b near the line between a and c ? */
 boom(1200);
 C double tmp = hypot((b-a) - (c-a)*(dot(b-a, c-a)/dot(c-a, c-a)));
 DD fprintf(stderr, "\t\tin_line2 (0,0) (%g,%g) (%g,%g) = %g\n",
			(b-a)(0), (b-a)(1), (c-a)(0), (c-a)(1), tmp);
 return tmp<sqrt(min(hypot(c-a), 1)) ? 1 : 0;
}


LOCAL int in_line(C vector2& a, C vector2& b, C vector2& c)
{	/* Put them into order... */
 boom(1300);
 C double ac = hypot(a-c);
 C double ab = hypot(a-b);
 C double bc = hypot(b-c);
 if(ac>bc && ac>ab)
	return in_line2(a, b, c);
 else if(bc>ac && bc>ab)
	return in_line2(b, a, c);
 return in_line2(b, c, a);
}


LOCAL BOOL four_in_line(C vector2& a, C vector2& b, C vector2& c, C vector2& d, C vector2& sz)
{	/* This is a component-by-component division */
 boom(1400);
 DD fprintf(stderr, "\tFIL (%g,%g), (%g,%g), (%g,%g), (%g,%g)\n",
			scale(a, sz)(0), scale(a, sz)(1),
			scale(b, sz)(0), scale(b, sz)(1),
			scale(c, sz)(0), scale(c, sz)(1),
			scale(d, sz)(0), scale(d, sz)(1));
 C int ntrip = in_line(scale(a, sz), scale(b, sz), scale(c, sz))
		+ in_line(scale(a, sz), scale(b, sz), scale(d, sz))
		+ in_line(scale(a, sz), scale(c, sz), scale(d, sz))
		+ in_line(scale(b, sz), scale(c, sz), scale(d, sz));
 DD fprintf(stderr, "\t\t\t\t%d triplets are in line\n", ntrip);
 return ntrip>2;
}


/* Finds a region. centered around ss_ctr, that includes most
of the interesting measurements.  nss is a hint, only, about
how many points should be considered. */
LOCAL vector2 find_ss_box(C sortedstuff& ss, int nss, C vector2& ss_ctr)
{
 boom(1500);
 int i, j;
 double max0 = 0.01, max1 = 0.01;
 for(j=i=0; j<nss && i<ss.n(); i++)
	if(finite(ss[i].y))	{
		C double t0 = fabs(ss[i].v(0) - ss_ctr(0));
		C double t1 = fabs(ss[i].v(1) - ss_ctr(1));
		if(t0 > max0)	max0 = t0;
		if(t1 > max1)	max1 = t1;
		j++;
		}
 return vector2(max0, max1);
}


/* Find points that are not in a line of four */
LOCAL int *select_not_in_line(C sortedstuff& s)
{
 C vector2 sz = find_ss_box(s, 8, s[0].v);
 int *good = int1alloc(s.n());
 assert(good);
 int i;
 boom(1600);
 for(i=0; i<s.n(); i++)	{
	good[i] = TRUE;
	int j;
	for(j=0; j<i; j++)
	  if(good[j])	{
		if(hypot(scale(s[i].v-s[j].v, sz)) < 0.001)	{
			good[i] = FALSE;
			break;
			}
		int k;
		for(k=0; k<j && good[i]; k++)
		  if(good[k])	{
			int l;
			for(l=0; l<k; l++)
			  if(good[l]
				 && four_in_line(s[i].v, s[j].v, s[k].v, s[l].v, sz*0.01))	{
					good[i] = FALSE;
					break;
					}
			}
		}
	}

 return good;
}




LOCAL int count_not_in_line(C sortedstuff& s)
{
 boom(1700);
 int *good = select_not_in_line(s);
 if(!good)
	return -1;
 int i, count;
 for(count=i=0;i<s.n() && count<6;i++)
	if(good[i])
		count++;
 int1free(good);
 return count>=6 ? i : -1;
}




struct maybe_s	{
	vector2 x;
	BOOL trustworthy;
	double y0;
	vector2 x0;
	matrix22 curv;
	vector2 slope;
	double y() C;
	maybe_s();
	};

maybe_s::maybe_s()
 : y0(quiet_nan(0))
{
}


double maybe_s::y() C
{
 C vector2 dx = x-x0;
 return quadratic_form(dx, curv) + dot(dx, slope) + y0;
}



/* returns a vecpair of NaNs if a parabolic approximation is non useful. */
LOCAL vecpair fit_dir_parab(C sortedstuff& s,
							double dely, double T,
							maybe_s *maybe)
{
 boom(1800);
 double m[6][6], beta[6];
 if(s.n() < 6)	{
	D fprintf(stderr, "\tNot 6 points!!! n=%d\n", s.n());
	return vecpair();
	}
 C int count = count_not_in_line(s);
 if(count < 0)	{
	D fprintf(stderr, "\tNot 6 good!!!: %d\n", count);
	return vecpair();
	}

 int i;
 {
 int j;
 for(j=0; j<6; j++)	{
	int k;
	for(k=0; k<6; k++)
		m[j][k] = 0;
	beta[j] = 0;
	}
 }
 int used = 0;
 double big = -infinity();
 for(i=0; i<s.n(); i++)	{
	if(!finite(s[i].y))
		continue;
	if(i>=count && used>6 && !(s[i].y<big+T*logrand()))
		continue;
	if(s[i].y > big)
		big = s[i].y;

	double x[6];
	x[0] = xx(s[i].v(0)-s[0].v(0));
	x[1] = 2*(s[i].v(0)-s[0].v(0))*(s[i].v(1)-s[0].v(1));
	x[2] = xx(s[i].v(1)-s[0].v(1));
	x[3] = (s[i].v(0)-s[0].v(0));
	x[4] = (s[i].v(1)-s[0].v(1));
	x[5] = 1/dely;
	int j;
	for(j=0; j<6; j++)	{
		int k;
		for(k=0; k<6; k++)
			m[j][k] += x[j]*x[k];
		beta[j] += x[j]*(s[i].y-s[0].y);
		}
	used++;
	}
 if(used < 6)	{
	fprintf(stderr, "Not 6 good!  Used=%d, count=%d, s.n()=%d\n",
		used, count, s.n());
	return vecpair();
	}

 if(!invert(m, 6))	{
	fprintf(stderr, "\tInvert fails!.   Points follow:\n");
	return vecpair();
	}

 double abcdef[6];
 for(i=0;i<6;i++)	{
	int k;
	abcdef[i] = 0;
	for(k=0;k<6;k++)
		abcdef[i] += m[i][k]*beta[k];
	}
 D fprintf(stderr, "\tsolution (%g, %g, %g)^2 + (%g,%g) + %g + %g\n",
					abcdef[0], abcdef[1], abcdef[2],
					abcdef[3], abcdef[4], abcdef[5],
					s[0].y);

 matrix22 curv(abcdef[0], abcdef[1], abcdef[1], abcdef[2]);

 vector2 evec1, evec2;
 jacobi_step(curv, &evec1, &evec2);
 C double eval1 = quadratic_form(evec1, curv);
 C double eval2 = quadratic_form(evec2, curv);

 vector2 downhill(-abcdef[3], -abcdef[4]);
 D fprintf(stderr, "\t\t\tdownhill= %g %g\n", downhill(0), downhill(1));

 if(eval1+2*eval2<0 || eval2+2*eval1<0)	{
	/* The eigenvalues are far from positive definite.  We cannot
		trust any parabolic approximation. */
	return vecpair();
	}

 if(dot(downhill, evec1) < 0)
	evec1 *= -1;

 if(dot(downhill, evec2) < 0)
	evec2 *= -1;

 evec1 *= sqrt(fabs(dely/eval1));	/* vectors have length to change chi-squared by about dely. */
 evec2 *= sqrt(fabs(dely/eval2));

 D fprintf(stderr, "\t\t\tevec=(%g, %g) (%g, %g)\n",
			evec1(0), evec1(1), evec2(0), evec2(1));

 /* Down here, there is some reason to believe that the
 eigenvalues mean *something*... */
 matrix22 invc;
 {
 C double min_eval = min(eval1, eval2);
 if(min_eval < 0)	{		/* Force the matrix to be positive definite, in an arbitrary manner. */
	curv -= 2 * min_eval * matrix22::identity();
	/* The minimum is probably wrong, so beware... */
	maybe->trustworthy = FALSE;
	}
 else	maybe->trustworthy = TRUE;

 fprintf(stderr, "curv="); m22print(stderr, curv);
 double det;
 invc = inverse(curv, &det);

	/* Strong cancellations - minimum is doubtful: */
 if(det < 0.1*curv(0,0)*curv(1,1))
	maybe->trustworthy = FALSE;
 }

 C vector2 randvec = evec1 * normalrand() * sqrt(T/dely)
				+ evec2 * normalrand() * sqrt(T/dely);
 /* Calculate where the minimum ought to be, if the surface were
		truly parabolic. Add in a random jiggle (r1&r2) so
	that we explore the region with temperature T. */
 maybe->x = invc*downhill*0.5 + s[0].v + randvec;

 maybe->slope = vector2(abcdef[3], abcdef[4]);
 maybe->y0 = abcdef[5] + s[0].y;
 maybe->curv = curv;
 maybe->x0 = s[0].v;

 D fprintf(stderr, "\t\tmaybe=(%g, %g) incl rand=(%g, %g)\n",
			maybe->x(0), maybe->x(1), randvec(0), randvec(1));
 DD fprintf(stderr, "\t\ticurv=((%g, %g),(%g, %g))\n", invc(0,0), invc(0,1),
								invc(1,0), invc(1,1));

 return vecpair(evec1, evec2);
}





LOCAL double restrict_scalar(double x, double ctr, double sz, double efac)
{
 assert(efac > 0);
 BOOM;
 return fabs((x-ctr)/(sz*efac));
}


LOCAL BOOL restrict_vector(vector2 *v, C vector2& ctr, C vector2& ss_sz, double efac)
{
 boom(1900);
 C double lfac = max(restrict_scalar((*v)(1), ctr(0), ss_sz(1), efac),
 					 restrict_scalar((*v)(0), ctr(0), ss_sz(0), efac));
 if(lfac < 1)
	return FALSE;

 *v = ctr + (*v-ctr)*(1.0/lfac);
 return TRUE;
}



int is_repeat_point(C sortedstuff& ss, C vector2& maybe_min, double howclose)
{
 C vector2 ss_sz = find_ss_box(ss, 6, ss[0].v);
 int i;
 int overlaps = 0;
 BOOM;
 for(i=0; i<ss.n(); i++)
	if(hypot(scale(maybe_min-ss[i].v, ss_sz)) < howclose)
		overlaps++;
 return overlaps;
}


double *expandout(C double * pr, C double *sz, int n, double er)
{
 double *d = double1alloc(n);
 assert(d);
 int i;
 for(i=0; i<n; i++)	{
	d[i] = pr[i]*sz[i]*er;
	}

 return d;
}



struct explore_ret	{
	double initial;
	double result;
	double dely_inc;
	float how_interesting;
	matrix22 m;

	explore_ret(): result(quiet_nan(0)), initial(quiet_nan(0)),
					dely_inc(0), how_interesting(0) {};
	explore_ret(C double r, double init, int inc, float h_i,
				C matrix22& mm);
	};

explore_ret::explore_ret(C double r, double init, int inc, float h_i,
							C matrix22& mm)
: result(r), initial(init), dely_inc(inc), how_interesting(h_i),
 m(mm)
{}



struct w_explore_t	{
	double *pc;
	double *d0, *d1;
	int n;
	double (*funk)(C double *, int, void *, int, int, int);
	void *c;
	int nc;
	int copy;
	double dely, T;
	int iteration;

	public:
	w_explore_t();
	~w_explore_t();
	w_explore_t(double *pc_, double *d0_, double *d1_,
						C double *sz,
						int n_,
						double (*funk_)(C double *, int, void *, int, int, int),
						void *c_, int nc_, int copy_, double dely_,
						double T_, int iter_);
	explore_ret *go();
	};

w_explore_t::~w_explore_t()
{ double1free(d0); double1free(d1); }



w_explore_t::w_explore_t()
: pc(0), d0(0), d1(0), n(0), funk(0), c(0), nc(0), copy(-1),
  dely(quiet_nan(0)), T(quiet_nan(0)), iteration(-1)
{;}

w_explore_t::w_explore_t(double *pc_, double *d0_, double *d1_,
				C double *sz, int n_,
				double (*funk_)(C double *, int, void *, int, int, int),
						void *c_, int nc_, int copy_, double dely_,
						double T_, int iter_)
:pc(pc_), d0(expandout(d0_, sz, n_, 1.0)),
	d1(expandout(d1_, sz, n_, 1.0)), n(n_), funk(funk_),
	c(c_), nc(nc_), copy(copy_),
	dely(dely_), iteration(iter_), T(T_)
{
 assert(pc && d0 && d1);
 assert(n > 0);
 assert(funk);
 assert(copy >= 0);
 assert(finite(dely));
 assert(d0 && d1);
 assert(fabs(hypot(d0_, n) - 1) < 0.001);
 assert(fabs(hypot(d1_, n) - 1) < 0.001);
 assert(dot(d0_, d1_, n) < 0.01);
 assert(T > 0);
}


void *w_explore(void *p)
{
 w_explore_t *a = (w_explore_t *)p;
 assert(a);
#ifdef _REENTRANT
 sigset_t mask;
 sigemptyset(&mask);
 sigaddset(&mask, SIGALRM);
 thr_sigsetmask(SIG_BLOCK, &mask, 0);
#endif
 return (void *)(a->go());
}



double high_value(C sortedstuff& ss, int nss, double T)
{
 double big = -infinity();
 int used = 0;
 int i;
 for(i=0; i<ss.n() && used<nss; i++)
	if(finite(ss[i].y) && ss[i].y>big)	{
		big = ss[i].y;
		used++;
		}

 return big + 0.3*T;
}




LOCAL double eval_scramble(C matrix22& x)
{
 return (x - matrix22::identity()).norm_2();
}


/* Get as close to an identity matrix as you can by renaming vectors or
 by reversing their direction.
 These operations make no difference in the final result. */
LOCAL matrix22 unscramble_matrix(C matrix22& x)
{
 C matrix22 op_np = matrix22(-1, 0, 0, 1);	/* Negate the top row. */
 C matrix22 op_pn = matrix22(1, 0, 0, -1);	/* Negate the bottom row. */
 C matrix22 op_swap = matrix22(0, 1, 1, 0);	/* Swap rows */
 double best = infinity();
 matrix22 bestmat;
 int i;
 for(i=0; i<(1<<3); i++) {
	matrix22 q = x;
	if(i & 1)
		q = op_np * q;
	if(i & (1<<1))
		q = op_pn * q;
	if(i & (1<<2))
		q = op_swap * q;
	C double howgood = eval_scramble(q);
	if(howgood < best) {
		best = howgood;
		bestmat = q;
		}
	}
 return bestmat;
}





explore_ret *w_explore_t::go()
{
 int dely_inc = -1;
 boom(1);
 sortedstuff ss(6+LOTS, T);
 assert(double1test(d0) == 0);
 assert(double1test(d1) == 0);

 C double initial = ss.add(Try(funk, c, nc, copy, iteration,
				pc, d0, d1, n, vector2(0.0, 0.0))).y;

 if(ss.n() == 0)
	return 0;

 C min_xf a = explore_inner(&ss, funk, c, nc, copy, iteration,
				pc, d0, d1, n, dely, T, vector2(1, 0.0), ss[0]);
 if(!finite(a.xf) || !finite(a.est_min) || ss.n()==0)
	return 0;

 /* Start from best result of first explore_inner() */
 C stuff cross = ss[0];
 C min_xf b = explore_inner(&ss, funk, c, nc, copy, iteration,
				pc, d0, d1, n, dely, T, vector2(0.0, 1), cross);
 if(!finite(b.xf) || !finite(b.est_min) || ss.n()==0)
	return 0;

 boom(6);
 /* Now, we work relative to the best result of the second explore_inner(). */
 {
 /* The fancy stuff in setting diag is to assure that it is not on one of the
   lines that has already been searched. */
 C vector2 diag(minmax(a.est_min-0.1, -a.est_min, a.est_min-3),
			fabs(b.est_min)>0.1
				? b.est_min :
				0.5*b.xf*(intrand(2) ? 1 : (-1)));
 C stuff ssdiag = ss.add(Try(funk, c, nc, copy, iteration, pc, d0, d1, n, diag));
 if(!(ssdiag.y < ss[0].y+30*dely))	{
	C vector2 newdiag = perp_to(diag-ss[0].v)*0.5 + ss[0].v;
	ss.add(Try(funk, c, nc, copy, iteration, pc, d0, d1, n, newdiag));
	}
 }
 boom(7);


 /* Now, we calculate where the minimum ought to be.
	We then go in that direction.  If things improve, we
	make use of the new information, and keep stepping
	towards our improving estimate of the minimum. */

 int cost = 0;
 BOOL again = FALSE;
 maybe_s testpoint;
 vecpair newdirs;
 double efac = exploredFac;
 BOOL do_nonparab = TRUE, had_success = FALSE;
 int parab_tries = 0, parab_success = 0;
 do	{
	again = FALSE;
	boom(2);

	vecpair tmpdirs = fit_dir_parab(ss, dely, T, &testpoint);
	BOOL been_there = is_repeat_point(ss, testpoint.x, 0.03);
	DD fprintf(stderr, "\t[copy=%1d]flags: f(t):%d, f(mm):%d, tm:%d, bt:%d\n",
				copy, finite(tmpdirs), finite(testpoint.x),
						testpoint.trustworthy, been_there);
	if(finite(tmpdirs) && finite(testpoint.x)
			&& (!been_there || had_success && testpoint.trustworthy))	{

		/* Don't let it go too far outside the area that has been explored: */
		C vector2 ss_sz = find_ss_box(ss, 8, ss[0].v);
		BOOL restricted = restrict_vector(&testpoint.x, ss[0].v, ss_sz,
											efac*(testpoint.trustworthy ? 1.0 : exploredFac_notrust));
		D fprintf(stderr, "\t[copy=%1d]maybe_min = (%g,%g)%s\n",
						copy, testpoint.x(0), testpoint.x(1),
						restricted ? "(restricted)" : "");

		C double lowest = ss[0].y;
		C double highest = high_value(ss, 6, T);

		stuff sstmp = Try(funk, c, nc, copy, iteration, pc, d0, d1, n, testpoint.x);
		cost++;
		parab_tries++;
		boom(8);

		if(isnan(sstmp.y))	{
			fprintf(stderr, "WARN: a_x.c: NaN when exploring parabolic prediction\n");
			break;
			}

		if(sstmp.y < ss[0].y + 0.1*T)	{
			D fprintf(stderr, "\t[copy=%1d]Success!!(%g -> %g)\n", copy, ss[0].y, sstmp.y);
			had_success = TRUE;
			parab_success++;
			}

		if(sstmp.y < ss[0].y + 0.01*(highest-lowest) + 0.1*T)
			dely_inc += 0.5/cost;


		do_nonparab = fabs(sstmp.y - testpoint.y()) > 0.1*T+0.03*(highest-lowest);
		fprintf(stderr, "[copy=%1d iter=%1d] y=%g vs. prediction=%g; do_nonparab=%d\n",
			copy, iteration,
							sstmp.y, testpoint.y(), do_nonparab);

		if(!do_nonparab)	{	/* Was the last fit successful? */
			D fprintf(stderr,
						"\t[copy=%1d iter=%d]parabolic directions: (%g,%g),(%g,%g)\n",
							copy, iteration,
							tmpdirs.v0(0), tmpdirs.v0(1),
							tmpdirs.v1(0), tmpdirs.v1(1));
			newdirs = tmpdirs;			/* if so, adopt the result. */
			efac += exploredFac;
			dely_inc += 0.7/cost;
			}
		else
			efac /= 2;

		ss.add(sstmp);

		if(sstmp.y<lowest - 0.01*(highest-lowest) - 0.01*dely
						|| restricted && sstmp.y < lowest + 0.1*(highest-lowest)
								&& cost<=3
						|| hypot(ss[0].v-vector2(0,0))<1e-6
							&& sstmp.y < 0.3*highest+0.7*lowest
							&& cost<=3)	{

			DD fprintf(stderr, "\t[copy=%1d]Promising... Again.\n", copy);
			again = TRUE;
			}

	if(been_there > 1)
		break;
	}
	else if(!had_success)	do_nonparab = TRUE;

 boom(3);
 /* If we haven't tried a parabolic minimization, or if it has failed badly... */
	if(do_nonparab)	{
		C double lowest = ss[0].y;
		C double highest = ss[5].y;

		D fprintf(stderr, "\t[copy=%1d]nonparabolic L@(%g,%g)=%g, H@(%g,%g)=%g\n",
						copy,
						ss[0].v(0), ss[0].v(1), ss[0].y,
						ss[5].v(0), ss[5].v(1), ss[5].y);
		boom(9);
		stuff sstmp;
		vector2 maybe_dir;
		C vecpair tmp = get_nonparab_dirs(ss, dely, &maybe_dir);
		C vector2 mm = ss[0].v
				+ maybe_dir*logrand()
				+ (tmp.v0*normalrand()
					+ tmp.v1*normalrand())*sqrt(T/dely);
		sstmp = ss.add(Try(funk, c, nc, copy, iteration, pc, d0, d1, n, mm));
		cost++;

		if(sstmp.y < lowest + 0.3*T)	{
			D fprintf(stderr, "\t[copy=%d]success: %g->%g at (%g,%g)\n",
								copy, lowest, sstmp.y, sstmp.v(0), sstmp.v(1));
			had_success = TRUE;
			}

		again = TRUE;
		}
	} while(again && cost<6);
 BOOM;

 if(!finite(newdirs))	{
	D fprintf(stderr, "\t[copy=%1d]Need nonparabolic directions.\n", copy);
	newdirs = get_nonparab_dirs(ss, dely);
	D fprintf(stderr, "\t[copy=%1d iter=%d]nonparab directions: (%g,%g) (%g,%g)\n",
					copy, iteration,
					newdirs.v0(0), newdirs.v0(1),
					newdirs.v1(0), newdirs.v1(1));
	}

 if(ss.n()>0 && hypot(ss[0].v) > 0.5)
	dely_inc++;
 if(ss.n()>6 && !finite(ss[ss.n()-2].y))	/* Infinite... */
	dely_inc--;
 if(ss.n()>0 && dely < fabs(ss[0].y)*sqrt(DBL_EPSILON))
	dely_inc++;

 C double ynext = go_accept_step(ss, pc, d0, d1, n, T);

 {
 matrix22 m(newdirs.v0(0), newdirs.v0(1), newdirs.v1(0), newdirs.v1(1));
 C double annealfudge = dely/(dely + 10*T);
	/* Annealfudge is small when in simulated annealing mode. */
 /* One generally wants to visit different parameters: things are less
	interesting after they have been visited: */
 float how_i = -3;
 if(finite(newdirs))	{

	/* Parameters that were strongly rotated (mixed) are interesting... */
	/* If directions change size dramatically, they are interesting... */
	/* Note that an exchange of the two directions is not interesting:
	m is uninteresting if it is an identity matrix or the transpose of one. */
	how_i += eval_scramble(unscramble_matrix(m));

	/* Things that move a lot are interesting... */
	if(finite(ss[0].v))
		how_i += annealfudge*asinh(quadratic_form(ss[0].v, m)/T);
	}

	/* Things that decrease y a lot are interesting... */
 if(finite(initial) && finite(ss[0].y))	{
	C float tmp = asinh((initial-ss[0].y)/T) * annealfudge;
	how_i += tmp;
	}

 if(parab_tries >0)	/* Nonparabolic directions need more attention. */
	how_i += (parab_tries - parab_success)/(double)parab_tries;

 return new explore_ret(ynext, initial, dely_inc, how_i, m);
 }
}













class avgd_thing	{
	double sum;
	double wt;

	public:
	double decay_fac;
	double weight() C	{return wt;}
	double val() C {return sum/wt;}
	operator double() C	{return val();}
	avgd_thing(double value, double w, double r=1.0)
		: sum(value*w), wt(w), decay_fac(r) {;}
	avgd_thing(): sum(0.0), wt(0.0), decay_fac(quiet_nan(0)) {;}
	double update(double nv, double nw=1.0);
	};

/* Deemphasize the unusual values: */
class robust_avgd_thing : public avgd_thing	{
	public:
	robust_avgd_thing(double value, double w, double r=1.0)
		: avgd_thing(value, w, r)	{;}
	robust_avgd_thing()	{;}
	robust_avgd_thing(C avgd_thing& a) : avgd_thing(a.val(), a.weight(), a.decay_fac)	{;}
	double update(double nv, double nw = 1.0);
	};


double robust_avgd_thing::update(double nv, double nw)
{
 C double old = val();
 if(nv>2*old || nv<0.5*old)
	nw /= 10;
 return avgd_thing::update(nv, nw);
}


double avgd_thing::update(double nv, double nw)
{
 sum += nv*nw;
 wt += nw;
 sum *= decay_fac;
 wt *= decay_fac;
 return val();
}






class interest_s	{
	int n;
	avgd_thing avg;
	BOOL free_int;
	float *interest, *adder;

	public:
	void print(FILE *fp) C;
	void update(C double *pi, int n_, float val);
	interest_s(float *interest_, int n_);
	~interest_s();
	void make_boring(int i);
	float operator[](int i) C { return interest[i]+adder[i];}
	double of_col(C double *p, int n_) C;
	BOOL at_least_some(C double *p, int n_) C;

	int check() C;

	private:
	interest_s(C interest_s&);
	interest_s();
	};


int interest_s::check() C
{
 if(float1test(adder) != 0)	{
	fprintf(stderr, "ERR: a_x.c: bad adder array: %d\n", float1test(adder));
	return 0;
	}
 if(free_int)	{
	if(float1test(interest) != 0)	{
		fprintf(stderr, "ERR: a_x.c: bad interest array: %d\n", float1test(interest));
		return 0;
		}
	}
 int i;
 for(i=0; i<n; i++)	{
	if(isnanf(interest[i]) || isnanf(adder[i]))	{
		fprintf(stderr, "ERR: a_x.c: fails interest consistency check: %g %g\n", interest[i], adder[i]);
		return 0;
		}
	}
 return 1;
}


double interest_s::of_col(C double *p, int n_) C
{
 int j;
 double x = 0;
 assert(n == n_);

 for(j=0; j<n; j++)	{
	assert(!isnanf(interest[j]));
	assert(!isnanf(adder[j]));
	if(p[j] != 0)	/* We need 0*Inf == 0 */
		x += (interest[j]+adder[j]) * xx(p[j]);
	}

 return x;
}



BOOL interest_s::at_least_some(C double *p, int n_) C
{
 int j;
 assert(n == n_);
 C float minf = -infinityf();

 for(j=0; j<n; j++)	{
	assert(!isnanf(interest[j]));
	assert(!isnanf(adder[j]));
	if(p[j]!=0 && !finite(interest[j]+adder[j]))
		return FALSE;
	}

 return TRUE;
}


void interest_s::make_boring(int i)
{ interest[i] = -infinityf(); adder[i] = -infinityf(); }


interest_s::~interest_s()
{
 float1free(adder);
 if(free_int)
	float1free(interest);
}


interest_s::interest_s(float *interest_, int n_)
 : interest(interest_), n(n_), free_int(FALSE),
	avg(0.0, 1.0, exp(-1.0/n)),
	adder(float1alloc(n_))
{
 int i;
 if(!interest)	{
	interest = float1alloc(n);
	free_int = TRUE;
	float1set(interest, 0.0);
	}

 assert(adder);
 for(i=0; i<n; i++)	{
	if(isnanf(interest[i]))	{
		fprintf(stderr, "ERR: a_x.c: interest[%d]==NaN.  "
				"Uninteresting things should be -Inf.\n", i);
		exit(3);
		}
	adder[i] = interest[i]/100.0;
	assert(!isnanf(adder[i]));
	}
}


void interest_s::print(FILE *fp) C
{
 int i;
 if(!fp)
	return;
 for(i=0; i<n; i++)
	fprintf(fp, "%.3f ", interest[i]);
 fprintf(fp, "\n");
 fflush(fp);
}


void interest_s::update(C double *pi, int n_, float val)
{
 int i;
 assert(n == n_);
 assert(n > 0);
 D fprintf(stderr, "Updating interest: val=%g n=%d avg=%g\n",
						val, n, avg.val());
 avg.update(val);
 val -= avg.val() + 1;	/* The +1 means we avoid previously tried pairs. */

 float norm = 0;
 INNERLOOP(j, 0, n, norm += xx(pi[j]));
 assert(norm > 0);

 for(i=0; i<n; i++)	{
	C float frac = 0.5 * xx(pi[i])/norm + 0.5/n;
 	interest[i] *= 1-frac;
 	interest[i] += frac * val;
	assert(!isnanf(interest[i]));
	}
}




void print_dirset(double **p, C double *sz, int n, FILE *fp, int iter, double dely)
{
 boom(2300);
 assert(double2test(p) == 0);
 if(fp)	{
	fprintf(fp, "Dely= %g\n", dely);
	fprintf(fp, "# sizes (typical variations in each parameter):\n");
	{
	int j;
	for(j=0;j<n;j++)
		fprintf(fp, "%.3g%c", sz[j], j==n-1 ? '\n' : ' ');
	}


	fprintf(fp, "# Direction set.  Parameters across,\n");
	fprintf(fp, "# different vectors down.  iter=%d\n", iter);
	int i;
	for(i=0;i<n;i++)	{
		int j;
		for(j=0;j<n;j++)
			fprintf(fp, "%.4f%c", p[i][j], j==n-1 ? '\n' : ' ');
		}
	fprintf(fp, "\n");
	fflush(fp);
	}
}



void points_to_dirset(double **p, C double *pc, C double *sz,
				C interest_s& interest, int n)
{
 assert(interest.check());
 assert(double2test(p) == 0);
 assert(pc);

 int ii;
 for(ii=0; ii<n; ii++)
    if(finite(interest[ii]))	{
	int jj;
	for(jj=0; jj<n; jj++)	{
		if(!finite(p[ii][jj]) || !finite(pc[jj]) || !finite(sz[jj]))	{
			fprintf(stderr, "WARN: points_to_dirset: non-finite input[%d][%d]: %g %g %g %g\n",
					ii, jj, p[ii][jj], pc[jj], sz[jj]);
			exit(1);
			}

		/* We implicitly assume that sz[jj]==0 implies p[ii][jj]==pc[jj]. */
		p[ii][jj] = sz[jj]!=0 ? (p[ii][jj] - pc[jj])/sz[jj] : 0.0;
		if(fabs(p[ii][jj]) < 0.01)	{
			if(!(p[ii][jj] == 0))
				fprintf(stderr, "Setting p[%d][%d] = 0, "
						"was %g sz=%g\n", ii, jj,
						p[ii][jj], sz[jj]);
			p[ii][jj] = 0.0;
			}
		}
	}

 for(ii=0; ii<n; ii++)
    if(finite(interest[ii]))	{
	int jj;
	C double norm = hypot(p[ii], n);
	if(!(norm > 0))	{
		char buf[512];
		sprintf(buf, "zero(or NaN) length input vector %d, l=%g", ii, norm);
		print_pc(p[ii], n, stderr, "Vector: ");
		gpk_err(buf, 0);
		}

	for(jj=0; jj<n; jj++)
		p[ii][jj] /= norm;
	assert(norm>0 && finite(norm));
	assert(fabs(hypot(p[ii], n) - 1) < 0.001);

	for(jj=0; jj<ii; jj++)
		if(finite(interest[jj]))	{	/* Uninteresting things don't have to be orthonormalizable. */
			BOOL ok = orthonormalize(p[ii], p[jj], n);
			if(!ok)	{
				char buf[512];
				sprintf(buf, "ERR: cannot orthogonalize two input vectors %d and %d", ii, jj);
				gpk_err(buf, 0);
				}
			}
	}

 {
 int i;
 C double qn = quiet_nan(0);
 for(i=0; i<n; i++)
	p[n][i] = qn;
 }
}



void dirset_to_points(double **p, C double *pc, C double *sz, int n, double f)
{
 boom(2500);
 int ii;
 for(ii=n-1;ii>=0;ii--)	{
	int j;
	for(j=0;j<n;j++)
		p[ii+1][j] = pc[j] + sz[j]*p[ii][j]*f;
	}
 copya(p[0], pc, n);
}



avgd_thing calc_spread(double **p, C double *pc, C double *sz, int n,
						double (*funk)(C double *, int, void *, int, int, int),
						void *c, int nc, int Nproc,
						C interest_s& interest)
{
 assert(interest.check());
 boom(2600);
 int n_finite, ntried;
 assert(double2test(p) == 0);
 double *y = double1alloc(n+1);
 assert(y);
 int i;
 double *tmp = double1alloc(n);
 assert(tmp);

 for(ntried=n_finite=i=0; i<n && n_finite<num_for_decr; i++)	{
	if(interest.at_least_some(p[i], n))	{
		int k;
		for(k=0; k<n; k++)
			tmp[k] = pc[k] + sz[k]*p[i][k];
		fprintf(stderr, "INFO: checking spread of y values be exploring in the %d direction:", i);
		D print_pc(tmp, n, stderr, "SPREAD DIRECTION:");
 		y[i] = (*funk)(tmp, n, c, nc, 0, 0);

		if(finite(y[i]))
			n_finite++;
		ntried++;
		}
	else	y[i] = quiet_nan(0);
	}
 for(; i<=n; i++)
	y[i] = quiet_nan(0);
 if(ntried == 0)
	gpk_err("calc_spread: All parameters seem uninteresting.\n", 0);


 double1free(tmp);

 double yc = infinity();
 for(i=0; i<=n; i++)	{
	if(y[i] < yc)	{ yc = y[i]; }
	}
 if(!finite(yc))	{
	fprintf(stderr, "#WARN: cannot find finite starting values: P may be too widely spread (yc=%g, nfinite=%d).\n",
		yc, n_finite);
	print_pc(y, n, stderr, "#y: ");
	return avgd_thing();
	}

 double typdecr;
 for(n_finite=0,typdecr=0.0,i=0;i<n;i++)	{
	if(finite(y[i]))	{
		typdecr += sqrt(y[i]-yc);
		n_finite++;
		}
	/* We do a square of average of root to emphasize typical small changes
	at the expense of one huge difference. */
	D fprintf(stderr, "#y[%d]=%g, yc=%g, n=%d, typdecr=%g\n",
						i, y[i], yc, n, typdecr);
	}
 if(n_finite <= 0)
	gpk_err("can't find finite starting values", 0);

 C double spreadwt = 1-exp(-SpreadDecr*n_finite/(double)(n*n+AFew));

 D fprintf(stderr, "#Typdecr=%g, initially, with wt=%g, decay_fac=%g\n",
			xx(typdecr/n_finite), spreadwt, exp(-SpreadDecr/(n*n+AFew)));


 double1free(y);
 return avgd_thing(xx(typdecr/n_finite), spreadwt, exp(-SpreadDecr/(n*n+AFew)));
}



class itpair	{
	public:
	int left, right;
	itpair(int xxl, int xxr) : left(xxl), right(xxr) {;}
	itpair() : left(-1), right(-1) {;}
	BOOL unused() C	{return left<0 || right<0; }
	};

/* This makes a ordered list of which pairs to explore. */
class itlist	{
	int i, c, spread, n0, incr;

	int newinc( int dd);

	public:
	itlist(int nn);
	void first() { i = c = 0; spread = 1; incr = newinc(spread); }
	C itpair next();
	};

int itlist::newinc(int newspread)
{
 int x;
 for(x=(int)(sqrt(n0)); x>1; x--)	{
	/* X is not a factor of n0.  Then, either x is
	not a factor of newspread, or it takes a lot
	of hops to get across. */
		
	if(n0%x != 0  &&  (newspread%x != 0  ||  newspread/x > 4))
		return x;
	}
 return 1;
}


C itpair itlist::next()
{
 assert(incr>0 && incr<n0);
 C itpair rv(c, (c+spread)%n0);
 C int tc = (c+incr)%n0;
 BOOM;
 if(++i >= n0)	{
	i = 0;
	c = 0;
	spread = 1 + spread%(n0-1); 
	incr = newinc(spread);
	}
 else	c = tc;

 return rv;
}



itlist::itlist(int nn)
: n0(nn)
{ first(); }



LOCAL BOOL is_not_duplicate(int test, C itpair *pair, int np)
{
 int i;
 BOOM;
 for(i=0; i<np; i++)	{
	if(test==pair[i].left || test==pair[i].right)	{
		DD fprintf(stderr, "npd: %d duplicates pair[%d]=(%d,%d)\n", test, i,
						pair[i].left, pair[i].right);
		return FALSE;
		}
	}
 DD fprintf(stderr, "npd: %d is ok\n", test);
 return TRUE;
}



LOCAL BOOL is_not_duplicate(C itpair& x, C itpair *pair, int np)
{
 return is_not_duplicate(x.left, pair, np)
		&& is_not_duplicate(x.right, pair, np);
}



LOCAL itpair find_interesting_pair(double **p, int n,
				itlist& test, int ntry,
				C itpair *pair, int nprocessors,
				C interest_s& interest)
{
 assert(double2test(p) == 0);
 double bgst = -infinity();
 double x = quiet_nan(0), *xi = double1alloc(n);
 itpair best;
 int i, ii, nninf = 1;
 C double qn = quiet_nan(0);
 BOOM;
 assert(xi);
 INNERLOOP(j, 0, n, xi[j]=qn);
 for(ii=0,i=0; i<n*n && ii<=nninf && ii<ntry; i++)	{
	C itpair ti = test.next();
	assert(!ti.unused());
	if(!is_not_duplicate(ti, pair, nprocessors))
		continue;

	if(isnan(xi[ti.left]))	{
		xi[ti.left] = interest.of_col(p[ti.left], n);
		assert(!isnan(xi[ti.left]));
		C BOOL notboring = finite(xi[ti.left]);
		if(interest.at_least_some(p[ti.left], n) != notboring)	{
			fprintf(stderr, "ERR: xi[%d]=%g nb=%d als=%d\n",
						ti.left, xi[ti.left], notboring, interest.at_least_some(p[ti.left], n));
			interest.print(stderr);
			print_pc(p[ti.left], n, stderr, "p[ti.left]:");
			abort();
			}
		nninf += finite(xi[ti.left]) || xi[ti.left]>0;
		}
	if(isnan(xi[ti.right]))	{
		xi[ti.right] = interest.of_col(p[ti.right], n);
		assert(!isnan(xi[ti.right]));
		C BOOL notboring = finite(xi[ti.right]);
		assert(interest.at_least_some(p[ti.right], n) == notboring);
		nninf += finite(xi[ti.right]) || xi[ti.right]>0;
		}
	x = xi[ti.left] + xi[ti.right];

	DD fprintf(stderr, "Ipair: [%d,%d] score=%g\n",
					ti.left, ti.right, x);
	if(x > bgst)	{
		bgst = x;
		best = ti;
		assert(!best.unused());
		/*
		fprintf(stderr, "interesting pair: [%d,%d] score=%g\n",
					best.left, best.right, bgst);
		*/
		}

	if(finite(x) || x>0)
		ii++;
	}
 double1free(xi);
 if(best.unused())	{
	fprintf(stderr, "find_interesting_pair(p, %d, test, %d, pair, %d, int)\n",
					n, ntry, nprocessors);
	fprintf(stderr, "bgst=%g, x=%g, i=%d, ii=%d\n", bgst, x, i, ii);
	}
 assert(!best.unused());
 return best;
}



LOCAL void shrink_sz(double *sz, int n, double fac)
{
 int j;
 for(j=0;j<n;j++)
	sz[j] = fac*sz[j];
}


void print_move(C double *newp, C double *oldp, int n, FILE *fp)
{
 double *tmp = double1alloc(n);
 int i;
 assert(tmp);
 for(i=0;i<n;i++)
	tmp[i] = newp[i]-oldp[i];
 print_pc(tmp, n, fp, "#scaled_move: ");
 double1free(tmp);
}





LOCAL void update_Estore(double *Estore, double T, int how_rarely)
{
 if(*Estore>20*T || *Estore<0 || how_rarely*unirand()<1)	{
		/* Now and then, we just completely re-initialize Estore. */
	*Estore = T*logrand();
	}
 else	{
		/* We have a choice of stepping up by 0.5*de, or stepping down by same. */
	C double de = T*logrand()/sqrt(how_rarely);

	C double p1 = 1 / (1 + exp(de/T));	/* Probability of accepting the step up. */

	C double Edel1 = de*(unirand()<p1 ? 0.5 : -0.5);

	if(*Estore+Edel1 > 0)
		*Estore += Edel1;
	}
}







/* Rows of m correspond to the two direction vectors.  I.e.  m(0,0) and m(0,1) are one, and
m(1,0) and m(1,1) are the other. */

LOCAL void update_sz_dirset(C matrix22& mm, double *sz, double *pl, double *pr, int n)
{
 assert(pl != pr);
 D print_pc(pl, n, stderr, "p[l]:");
 D print_pc(pr, n, stderr, "p[r]:");
 assert(dot(pl, pr, n) < 0.01);

 C matrix22 m = unscramble_matrix(mm);

 C double c00 = m(0,0);
 C double c10 = m(1,0);
 C double c01 = m(0,1);
 C double c11 = m(1,1);
 int i;
 double nc00 = 0.0, nc01 = 0.0, nc10 = 0.0, nc11 = 0.0;
 double sl = 0.0, sr = 0.0, slr = 0.0;
 C double sum = 1.0;
 for(i=0; i<n; i++)	{
	C double npl = pl[i]*c00 + pr[i]*c01;
	C double npr = pl[i]*c10 + pr[i]*c11;
	C double nsz = sz[i] * sqrt((xx(npr) + xx(npl) + sum)
				/ (xx(pr[i]) + xx(pl[i]) + sum));
	nc00 += pl[i] * (npl*sz[i]/nsz);
	nc10 += pl[i] * (npr*sz[i]/nsz);
	nc11 += pr[i] * (npr*sz[i]/nsz);
	nc01 += pr[i] * (npl*sz[i]/nsz);
	sl += xx(pl[i]);
	sr += xx(pr[i]);
	slr += pl[i]*pr[i];
	sz[i] = nsz;
	}
 sl = sqrt(sl/n);
 sr = sqrt(sr/n);
 slr /= n;
 assert(slr < 0.1);
 C double ll0 = hypot(nc00, nc01);
 C double ll1 = hypot(nc10, nc11);
 nc00 /= ll0;
 nc01 /= ll0;
 nc10 /= ll1;
 nc11 /= ll1;

 C double slow = 0.5;
 int pass;
 for(pass=0; pass<10; pass++)	{
	/* Now, force orthogonality and normality. */
	C double dotprod = nc00*nc10 + nc01*nc11 + slr*(nc00*nc11+nc01*nc10);
	C double tc00 = nc00 - nc10 * 0.5 * slow * dotprod;
	C double tc10 = nc10 - nc00 * 0.5 * slow * dotprod;
	C double tc01 = nc01 - nc11 * 0.5 * slow * dotprod;
	C double tc11 = nc11 - nc01 * 0.5 * slow * dotprod;
	nc00 = tc00/hypot(tc00, tc01);
	nc10 = tc10/hypot(tc10, tc11);
	nc01 = tc01/hypot(tc00, tc01);
	nc11 = tc11/hypot(tc10, tc11);
	}
 nc00 /= sl;
 nc10 /= sl;
 nc01 /= sr;
 nc11 /= sr;
 for(i=0; i<n; i++)	{
	C double tmpl = pl[i]*nc00 + pr[i]*nc01;
	C double tmpr = pl[i]*nc10 + pr[i]*nc11;
	pl[i] = tmpl;
	pr[i] = tmpr;
	}
 DD print_pc(sz, n, stderr, "size after:");
}



double **amoeba_x_allfree(double **p,
				int n,
				double ftol,
				double (*funk)(C double *p, int n,
								void *c, int nc, int copy,
								int iter),
				int itmax,
				void *c,
				int nc,
				int nprocessors,
				double T,
				float *interest_)
{
#ifdef _REENTRANT
 nprocessors = abs(nprocessors);
#else
 nprocessors = 1;
#endif

 if(n <= 1)	{
	fprintf(stderr, "ERR: a_x.c: algorithm requires 2-D or higher minimization. You have n=%d.\n", n);
	return 0;
	}
 assert(p && *p);
 if(!(ftol >= 0))	{
	fprintf(stderr, "ERR: a_x.c: nonpositive tolerance=%g\n", ftol);
	return 0;
	}
 if(nprocessors <= 0)	{
	fprintf(stderr,
			"WARN: a_x.c: nonpositive number of processors=%d, setting to 1.\n",
			nprocessors);
	nprocessors = 1;
	}
 assert(double2test(p) == 0);

 if(2*nprocessors >= n  && nprocessors>1)	{
	fprintf(stderr, "#WARN: a_x.c: Too many processors.  Using %d.\n", n/2);
	nprocessors = n==2 ? 1 : (n-1)/2;
	}
 if(T < ftol/(2*n))	{
	T = ftol/(2*n);
	fprintf(stderr, "#WARN: a_x.c: T set too low: resetting to %g.\n", ftol/(2*n));
	}

 interest_s interest(interest_, n);

/* First, we find the center point. */
 double *pc = amoeba_center(p, n);
 assert(pc);
 D print_pc(pc, n, stderr, "initial pc:");


 /* Next, we get the size of each parameter's wiggle zone: */
 double *sz = amoeba_spread(p, n);
 assert(sz);

 {
 int i;		/* turn off all parameters with zero range. */
 for(i=0; i<n; i++)
	if(isnanf(interest[i]))	{
		fprintf(stderr, "ERR: a_x.c: interest[%d] is NaN\n", i);
		exit(1);
		}

 for(i=0; i<n; i++)
	if(!finite(sz[i]))	{
		fprintf(stderr, "ERR: a_x.c: sz[%d]=%g is not finite.\n", i, sz[i]);
		exit(1);
		}

 for(i=0; i<n; i++)
	if(!(sz[i] > 0))	{
		if(!interest_)	{
			interest.make_boring(i);
			sz[i] = quiet_nan(0);
			fprintf(stderr, "INFO: making parameter %d unintersting because of zero range.\n", i);
			}
		else if(finite(interest[i]))	{
			char buf[512];
			sprintf(buf,
				"parameter[%d] has nonpositive range(%g), "
				"but interest[%d]=%g is not -infinity.",
				i, sz[i], i, interest[i]);
			gpk_err(buf, 0);
			}
		}

 for(i=0; i<n; i++)	{
	assert(finite(sz[i]));
	assert(!isnanf(interest[i]));
	}
 assert(interest.check());
 } /* Here, we can guarentee that sz[i]>0 if interest!=-infinity.
	sz[i] is always finite, and interest[i] is not NaN.*/

 /* Then we set up the direction set.
	After here, until dirset_to_points(), p is orthonormal and scaled to unity. */
 points_to_dirset(p, pc, sz, interest, n);


 robust_avgd_thing spread;
 {
 int spt = 0;
 do	{
	assert(interest.check());
	spread = calc_spread(p, pc, sz, n, funk, c, nc, nprocessors, interest);
	if(!finite(spread))	{
		fprintf(stderr, "Shrinking p by 0.5\n");
		shrink_sz(sz, n, 0.5);
		}
	} while(!finite(spread) && spt++<5);
 if(!finite(spread))	{
	fprintf(stderr, "ERR: can't find good starting point set after %d tries.\n", spt);
	return 0;
	}
 }
 boom(3000);


 double **tmp_pc = double2alloc(nprocessors, n);
 double *tmp_y = double1alloc(nprocessors);
 assert(tmp_pc && tmp_y);
 {
  int i;
  for(i=0;i<nprocessors;i++)	{
	copya(tmp_pc[i], pc, n);
	tmp_y[i] = quiet_nan(0);
	}
 }

 FILE *afp = 0, *dfp = 0, *ifp = 0;
 afp = fopen("amoeba.tmp", "w");
 dfp = fopen("amoeba.dirset", "w");
 ifp = fopen("amoeba.interest", "w");
 assert(double2test(p) == 0);


 itlist testi(n), testj(n);
 int iter = 0, missed = 0;
 double dely = max(sqrt(SpreadFac*spread.val()*T), 2.5*T);
 C double dely0 = dely;
 C double dely_step = pow(3, 1.0/(n + AFew));
 itpair *pair = new itpair[nprocessors];
 assert(pair);
 int *fails = new int[nprocessors];
 assert(fails);
 {
  int i;
  for(i=0; i<nprocessors; i++)
	fails[i] = 0;
 }

 w_explore_t **w_explore_args = new w_explore_t* [nprocessors];
 double y_pc = infinity();
 double y_last = quiet_nan(0);
 double typ_decr = spread.val();
 double Estore = T;
 C double  coolrate_threshold = 1.0/((n+AFew)*sqrt(nprocessors));
 double coolrate = 1.5*coolrate_threshold;
 unsigned paircount = 0;
#ifdef _REENTRANT
 thread_t *tid = new thread_t[nprocessors];
#endif
 while(1)	{
	dely = max(dely, 1.5*T);
	if(finite(y_pc))
		dely = max(dely, fabs(y_pc)*1000*DBL_EPSILON);

	int copy;
	for(copy=0; copy<nprocessors && iter<itmax; copy++)
		/* Use fails[copy] here... */
		if(pair[copy].unused())	{
			paircount++;
			if(paircount%2 == 0)
				pair[copy] = find_interesting_pair(p, n, testi, n/2, pair, nprocessors,
									interest);
			else
				pair[copy] = find_interesting_pair(p, n, testj, 1, pair, nprocessors,
									interest);

			fprintf(stderr, "======New iter=%d for [copy=%1d] columns=(%d, %d) Dely=%g =====\n",
					iter, copy, pair[copy].left, pair[copy].right, dely);
			summarize_dir("===column.left is ", stderr, p[pair[copy].left], sz, n, 5);
			summarize_dir("===column.right is ", stderr, p[pair[copy].right], sz, n, 5);
			BOOM;



			copya(tmp_pc[copy], pc, n);
			tmp_y[copy] = y_pc;

			assert(pair[copy].left != pair[copy].right);
			assert(pair[copy].left>=0 && pair[copy].right>=0);
			assert(p[pair[copy].left] != p[pair[copy].right]);
			if(!(dot(p[pair[copy].left], p[pair[copy].right], n) < 0.01))	{
				fprintf(stderr, "ERR: pair[%d]=(%d,%d) nonorthogonal: %g\n",
					copy, pair[copy].left, pair[copy].right,
					dot(p[pair[copy].left], p[pair[copy].right], n));
				}
			assert(dot(p[pair[copy].left], p[pair[copy].right], n) < 0.01);

			BOOM;
			w_explore_args[copy] = new w_explore_t(tmp_pc[copy],
								p[pair[copy].left],
								p[pair[copy].right],
								sz,
								n, funk, c, nc, copy,
								dely, T, iter);
#ifdef _REENTRANT
			boom(11210+copy);
			C int cv = thr_create(NULL, 0, w_explore,
						(void *)(w_explore_args[copy]),
						0, &tid[copy]);
			boom(11220+copy);
			if(cv != 0)	{
				char buf[512];
				sprintf(buf, "thr_create fails: %d %s", cv, strerror(cv));
				gpk_err(buf, 0);
				}
#endif
			}
	explore_ret *status = 0;
#ifdef _REENTRANT
	thread_t joined;
	boom(40200);
	C int cv = thr_join((thread_t)0, &joined, (void **)&status);
	if(cv != 0)	{
		fprintf(stderr, "WARN: a_x.c: thr_join: %d, %s\n", cv, strerror(cv));
		break;
		}
	assert(cv==0);
	for(copy=0; copy<nprocessors; copy++)
		if(joined == tid[copy])
			break;
	assert(copy < nprocessors);
	tid[copy] = (thread_t)0;

#else
	status = (explore_ret *)w_explore((void *)(w_explore_args[copy]));
#endif
	delete w_explore_args[copy];
	w_explore_args[copy] = 0;

	if(!status)	{
		fprintf(stderr, "WARN: nil w_explore_t::go() -"
				" FUNK returns NaN {network failure?} [copy=%1d]. Continuing.\n",
				copy);
		pair[copy] = itpair();		/* Mark as a free slot. */
		fails[copy]++;
		continue;
		}
	else	fails[copy] = 0;

	if(dely > T+max(0, typ_decr)*5)
		status->dely_inc--;
	dely *= pow(dely_step, status->dely_inc );
	fprintf(stderr, "Dely updated to %g, inc=%g, T=%g, typ_decr=%g\n",
				dely, status->dely_inc, T, typ_decr);

	if(status->m.ok() && finite(status->m.determinant()))
		update_sz_dirset(status->m, sz, p[pair[copy].left], p[pair[copy].right], n);

	if(finite(status->how_interesting))	{
		interest.update(p[pair[copy].left], n, status->how_interesting);
		interest.update(p[pair[copy].right], n, status->how_interesting);
		}


	pair[copy] = itpair();		/* Mark as a free slot. */
	boom(40000+copy);

	D fprintf(stderr, "explore: [copy=%1d] from %g to %g (last=%g)\n",
						copy, status->initial, status->result, y_last);



	if(isnan(status->result))	{
		fprintf(stderr, "WARN: NaN returned from w_explore_t::go() -"
					" can't improve chi-squared.  Continuing.\n");
		continue;
		}

	tmp_y[copy] = status->result;
	delete status;

	if(finite(y_pc) && finite(tmp_y[copy]))
		coolrate += (asinh((y_pc - tmp_y[copy])/T) - coolrate)/n;
	fprintf(stderr, "coolrate=%g\n", coolrate);

	/* The fudge factor is there to handle the case where the definition of
	   chi-squared changes by more than T.   Bootstrap resampling is one
	   example.  Here, we bring (pc,y_pc) into thermal contact with
	   (tmp_pc[copy], tmp_y[copy]).   If we made a good step, the
	   newly calculated values found in tmp_pc and tmp_y will likely
	   be accepted into pc and y_pc.  */
	C double fudge = 1 + pow(2.0, missed-11);
	if(tmp_y[copy]-Estore*fudge < y_pc)	{
		D fprintf(stderr, "Explore: [copy=%1d] improved-from %g to %g (Estore=%g)\n",
						copy, y_pc, tmp_y[copy], Estore);
		if(nprocessors>1)
			fprintf(stderr, "\t[copy=%d] is the new best\n", copy);
		print_move(tmp_pc[copy], pc, n, afp);
		fprintf(afp, "# E= %g ; dely= %g; iter=%d; Estore=%g; processor=%d\n",
				tmp_y[copy], dely, iter, Estore, copy);
		copya(pc, tmp_pc[copy], n);
		if(finite(y_pc))
			Estore += y_pc - tmp_y[copy];
		y_pc = tmp_y[copy];
		print_pc(pc, n, afp);
		iter++;
		missed = 0;
		}
	else
		missed++;

	update_Estore(&Estore, T, 2*nprocessors);

	if(finite(y_last) && finite(tmp_y[copy]))
		typ_decr = typ_decr*(1-1.0/n) + (y_last - tmp_y[copy])/(double)n;
	if(finite(tmp_y[copy]))
		y_last = tmp_y[copy];

	if(iter%n==n-1 && ftol>0 && typ_decr*n<ftol && coolrate>=0)
		iter = itmax;

	fprintf(stderr, iter>=itmax ? "END!\n" : "\n");

	if((iter+1)%(n/2) == 0 || DEBUG_DIRSET)	{
		print_dirset(p, sz, n, dfp, iter, dely0);
		interest.print(ifp);
		}
	}
 double2free(tmp_pc);
 delete [] pair;
 delete [] fails;
 delete [] w_explore_args;
#ifdef _REENTRANT
 delete [] tid;
#endif
 fprintf(stderr, "Closing files\n");
 if(afp)
	fclose(afp);
 if((iter+1)%(n/nprocessors) > 3)
	print_dirset(p, sz, n, dfp, iter, dely0);
 if(dfp)
	fclose(dfp);
 if(ifp)
	fclose(ifp);


 dirset_to_points(p, pc, sz, n, pow(dely0/dely, 0.25));

 fprintf(stderr, "Freeing spaces\n");
 free(pc);
 free(sz);
 return p;
}


