/*** File libwcs/matchstar.c
 *** February 28, 2001
 *** By Doug Mink, dmink@cfa.harvard.edu
 *** Harvard-Smithsonian Center for Astrophysics
 */

/* StarMatch (ns, sx, sy, ng, gra, gdec, gx, gy, tol, wcs, nfit, debug)
 *  Find shift, scale, and rotation of image stars to best-match reference stars
 *
 * ReadMatch (filename, sx, sy, gra, gdec, debug)
 *  Read in x, y, RA, and Dec of pre-match stars in image
 *
 * FitMatch (ns, sx, sy, ng, gra, gdec, gx, gy, tol, wcs, nfit, debug)
 *  Find shift, scale, and rotation of image stars to RA/Dec/X/Y matches
 *
 * vimoswcs_amoeba (wcs0) Set up temp arrays and call multivariate solver
 * chisqr (v) Compute the chisqr of the vector v
 * amoeba (p, y, ndim, ftol, itmax, funk, nfunk)
 *    Multivariate solver from Numerical Recipes
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* #include <malloc.h> */
#include <string.h>
#include "fitshead.h"
#include "vimoswcs.h"
#include "lvimoswcs.h"
#include "vimoswcscat.h"

#define NPAR 8
#define NPAR1 9

#define ABS(a) ((a) < 0 ? (-(a)) : (a))

static void vimoswcs_amoeba ();
extern void setnofit();
extern int getfilelines();

/* Statics used by the chisqr evaluator */
static double	*sx_p;
static double	*sy_p;
static double	*gra_p;
static double	*gdec_p;
static double	xref_p, yref_p;
static double	xrefpix, yrefpix;
static int	nbin_p;
static int	nfit;	/* Number of parameters to fit */
static int	pfit0 = 0;	/* List of parameters to fit, 1 per digit */
static int	cdfit = 0;	/* 1 if CD matrix has been fit */
static int	resid_refine = 0;
static int	minbin=2;		/* Minimum number of coincidence hits needed */
static int	vfit[NPAR1]; /* Parameters being fit: index to value vector
				1= RA,		  2= Dec,
				3= X plate scale, 4= Y plate scale
				5= rotation,	  6= second rotation (skew),
				7= optical axis X,8= optical axis Y */

/* Find shift, scale, and rotation of image stars to best-match reference stars
 * Get best match by finding which offsets between pairs of s's and g's
 * work for the most other pairs of s's and g's
 * N.B. we assume rotation will be "small enough" so that initial guesses can
 *   be done using just shifts.
 * Return count of total coincidences found, else 0 if none or -1 if trouble.
 */

int
StarMatch (ns, sx, sy, ng, gra, gdec, gx, gy, tol, vimoswcs, debug)

int	ns;		/* Number of image stars */
double	*sx;		/* Image star X coordinates in pixels */
double	*sy;		/* Image star Y coordinates in pixels */
int	ng;		/* Number of reference stars */
double	*gra;		/* Reference star right ascensions in degrees */
double	*gdec;		/* Reference star right ascensions in degrees */
double	*gx;		/* Reference star X coordinates in pixels */
double	*gy;		/* Reference star Y coordinates in pixels */
double	tol;		/* +/- this many pixels is a hit */
struct WorldCoor *vimoswcs;	/* World coordinate structure (fit returned) */
int	debug;

{
    double dx, bestdx, dxi;
    double dy, bestdy, dyi;
    int nmatch;
    int s, g, si, gi;
    int nbin;
    double *sbx, *sby;	/* malloced array of s stars in best bin */
    double *gbra, *gbdec;	/* malloced array of g stars in best bin */
    int peaks[NPEAKS+1];	/* history of bin counts */
    int dxpeaks[NPEAKS+1], dypeaks[NPEAKS+1]; /* history of dx/dy at peaks */
    int npeaks;		/* entries in use in peaks[] */
    int maxnbin, i, nmatchd;
    int minmatch;
    int *is, *ig, *ibs, *ibg;
    char rastr[16], decstr[16];
    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
    int bestbin;	/* Number of coincidences for refit */
    int pfit;		/* List of parameters to fit, 1 per digit */
    char vpar[16];	/* List of parameters to fit */
    char *vi;
    char vc;

    /* Do coarse alignment assuming no rotation required.
     * This will allow us to collect a set of stars that correspond and
     * establish an initial guess of the solution.
     */
    npeaks = 0;
    nmatch = 0;
    for (i = 0; i < NPEAKS; i++) {
	peaks[i] = 0;
	dxpeaks[i] = 0;
	dypeaks[i] = 0;
	}
    bestdx = 0.0;
    bestdy = 0.0;
    maxnbin = ns;
    if (ng > ns)
	maxnbin = ng;
    minmatch = 0.5 * ns;
    if (ns > ng)
	minmatch = 0.5 * ng;

    if (debug)
	fprintf (stderr,"Match history: nim=%d nref=%d tol=%3.0f minbin=%d minmatch=%d):\n",
		ns, ng, tol, minbin, minmatch);

    is = (int *) calloc (maxnbin, sizeof(int));
    ig = (int *) calloc (maxnbin, sizeof(int));
    ibs = (int *) calloc (maxnbin, sizeof(int));
    ibg = (int *) calloc (maxnbin, sizeof(int));
    for (s = 0; s < ns; s++) {
	for (g = 0; g < ng; g++) {
	    dx = gx[g] - sx[s];
	    dy = gy[g] - sy[s];
	    nbin = 1;
	    is[0] = s;
	    ig[0] = g;
	    for (gi = 0; gi < ng; gi++) {
		for (si = 0; si < ns; si++) {
		    if (si != s && gi != g) {
			dxi = ABS (gx[gi] - sx[si] - dx);
			dyi = ABS (gy[gi] - sy[si] - dy);
			if (dxi <= tol && dyi <= tol) {
			    /* if (debug)
				fprintf (stderr,"%d %d %d %d %5.1f %5.1f %5.1f %5.1f\n",
					g,s,gi,si,dx,dy,dxi,dyi); */
			    is[nbin] = si;
			    ig[nbin] = gi;
			    nbin++;
			    }
			}
		    }
		}
	    /* if (debug)
		fprintf (stderr,"%d %d %d %d %d\n", g,s,gi,si,nbin); */
	    if (nbin > 1 && nbin >= nmatch) {
		int i;
		nmatch = nbin;
		bestdx = (double) dx;
		bestdy = (double) dy;
		for (i = 0; i < nbin; i++) {
		    ibs[i] = is[i];
		    ibg[i] = ig[i];
		    }
	
		/* keep last NPEAKS nmatchs, dx and dy;
		 * put newest first in arrays */
		if (npeaks > 0) {
		    for (i = npeaks; i > 0; i--) {
			peaks[i] = peaks[i-1];
			dxpeaks[i] = dxpeaks[i-1];
			dypeaks[i] = dypeaks[i-1];
			}
		    }
		peaks[0] = nmatch;
		if (bestdx > 0.0)
		    dxpeaks[0] = (int) (bestdx + 0.5);
		else
		    dxpeaks[0] = (int) (bestdx - 0.5);
		if (bestdy > 0)
		    dypeaks[0] = (int) (bestdy + 0.5);
		else
		    dypeaks[0] = (int) (bestdy - 0.5);
		if (npeaks < NPEAKS)
		    npeaks++;
		if (debug)
		    fprintf (stderr,"%d: %d matches at image %d cat %d: dx= %d dy= %d\n",
			    npeaks, nmatch, s, g, dxpeaks[0], dypeaks[0]);
		}
	    if (nmatch > minmatch)
		break;
	    }
	if (nmatch > minmatch)
	    break;
	}

    /* if (debug) {
	int i;
	for (i = 0; i < npeaks; i++)
	    fprintf (stderr," %d bins at dx=%d dy=%d\n",
		    peaks[i], dxpeaks[i], dypeaks[i]);
	} */

    /* peak is broad */
    if (npeaks < 2 || peaks[1] == peaks[0]) {
	if (debug)
	    fprintf (stderr,"  Broad peak of %d bins at dx=%.0f dy=%.0f\n",
		     peaks[0], bestdx, bestdy);
	}

    /* too few hits */
    if (nmatch < minbin)
	return (nmatch);

    /* Get X and Y coordinates of matches from best binning */
    nmatchd = nmatch * sizeof (double);
    if (!(sbx = (double *) malloc (nmatchd)))
	fprintf (stderr," Could not allocate %d bytes for SBX\n", nmatchd);
    if (!(sby = (double *) malloc (nmatchd)))
	fprintf (stderr," Could not allocate %d bytes for SBY\n", nmatchd);
    if (!(gbra = (double *) malloc (nmatchd)))
	fprintf (stderr," Could not allocate %d bytes for GBRA\n", nmatchd);
    if (!(gbdec = (double *) malloc (nmatchd)))
	fprintf (stderr," Could not allocate %d bytes for GBDEC\n", nmatchd);
    for (nbin = 0; nbin < nmatch; nbin++) {
	sbx[nbin] = sx[ibs[nbin]];
	sby[nbin] = sy[ibs[nbin]];
	gbra[nbin] = gra[ibg[nbin]];
	gbdec[nbin] = gdec[ibg[nbin]];
	}

    /* Reset image center based on star matching */
    vimoswcs->xref = vimoswcs->xref + (bestdx * vimoswcs->xinc);
    vimoswcs->yref = vimoswcs->yref + (bestdy * vimoswcs->yinc);

    /* Fit WCS to matched stars */

    /* Provide non-parametric access to the star lists */
    sx_p = sbx;
    sy_p = sby;
    gra_p = gbra;
    gdec_p = gbdec;
    xref_p = vimoswcs->xref;
    yref_p = vimoswcs->yref;
    xrefpix = vimoswcs->xrefpix;
    yrefpix = vimoswcs->yrefpix;
    nbin_p = nbin;

    /* Number of parameters to fit from command line or number of matches */
    if (pfit0 != 0) {
	if (pfit0 < 3)
	    pfit = 12;
	else if (pfit0 == 3)	/* Fit center and plate scale */
	    pfit = 123;
	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
	    pfit = 1235;
	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
	    pfit = 12345;
	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
	    pfit = 123456;
	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
	    pfit = 1234578;
	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
	    pfit = 12345678;
	else
	    pfit = pfit0;
	}
    else if (nbin < 4)
	pfit = 12;
    else if (nbin < 6)
	pfit = 123;
    else
	pfit = 12345;

    /* Get parameters to fit from digits of pfit */
    sprintf (vpar, "%d", pfit);
    nfit = 0;
    vfit[0] = -1;
    for (i = 1; i < NPAR1; i++) {
	vc = i + 48;
	vi = strchr (vpar, vc);
	if (vi != NULL) {
	    vfit[i] = vi - vpar;
	    nfit++;
	    }
	else
	    vfit[i] = -1;
	}

    /* Set initial guesses for parameters which are being fit */
    xref0 = vimoswcs->xref;
    yref0 = vimoswcs->yref;
    xinc0 = vimoswcs->xinc;
    yinc0 = vimoswcs->yinc;
    rot0 = vimoswcs->rot;
    xrefpix0 = vimoswcs->xrefpix;
    yrefpix0 = vimoswcs->yrefpix;
    cd0[0] = vimoswcs->cd[0];
    cd0[1] = vimoswcs->cd[1];
    cd0[2] = vimoswcs->cd[2];
    cd0[3] = vimoswcs->cd[3];
    if (vfit[6] > -1)
	cdfit = 1;
    else
	cdfit = 0;

    /* Fit image star coordinates to reference star positions */
    vimoswcs_amoeba (vimoswcs);

    if (debug) {
	fprintf (stderr,"\nAmoeba fit:\n");
	ra2str (rastr, 16, xref0, 3);
	dec2str (decstr, 16, yref0, 2);
	fprintf (stderr,"   initial guess:\n");
	if (vfit[6] > -1)
	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
	else
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);

	ra2str (rastr, 16, vimoswcs->xref, 3);
	dec2str (decstr, 16, vimoswcs->yref, 2);
	fprintf (stderr,"\nfirst solution:\n");
	if (vfit[6] > -1)
	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
		     rastr,decstr,vimoswcs->cd[0],vimoswcs->cd[1],vimoswcs->cd[2],vimoswcs->cd[3]);
	else
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		     rastr,decstr,3600.0*vimoswcs->xinc,3600.0*vimoswcs->yinc,vimoswcs->rot);
	fprintf (stderr,"(%8.2f,%8.2f)\n", vimoswcs->xrefpix, vimoswcs->yrefpix);
	}

    /* If we have extra bins, repeat with the best ones */
    bestbin = nfit + 1;
    if (resid_refine && nmatch > bestbin) {
	double *resid = (double *) malloc (nmatch * sizeof(double));
	double *xe = (double *) malloc (nmatch * sizeof(double));
	double *ye = (double *) malloc (nmatch * sizeof(double));
	int i, j;
	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
	double mx, my, xsig, ysig, rsig, siglim;
	char vimoswcstring[64];
	double xsum = 0.0;
	double ysum = 0.0;
	double rsum = 0.0;
	double dmatch = (double)nmatch;
	double dmatch1 = (double)(nmatch - 1);

	/* Compute residuals at each star location */
	for (i = 0; i < nmatch; i++) {
	    pix2vimoswcs (vimoswcs, sbx[i], sby[i], &mx, &my);
	    xe[i] = (mx - gbra[i]) * 3600.0;
	    ye[i] = (my - gbdec[i]) * 3600.0;
	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
	    if (debug) {
		pix2vimoswcst (vimoswcs, sbx[i], sby[i], vimoswcstring, 64);
		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
		    i, sbx[i], sby[i], vimoswcstring, xe[i], ye[i], resid[i]);
		}
	    xsum = xsum + xe[i];
	    ysum = ysum + ye[i];
	    rsum = rsum + resid[i];
	    }

	/* Compute means and standard deviations */
	xmean = xsum / dmatch;
	ymean = ysum / dmatch;
	rmean = rsum / dmatch;
	xsumsq = 0.0;
	ysumsq = 0.0;
	for (i = 0; i < nmatch; i++) {
	    diff = xe[i] - xmean;
	    xsumsq = xsumsq + (diff * diff);
	    diff = ye[i] - ymean;
	    ysumsq = ysumsq + (diff * diff);
	    }
	xsig = sqrt (xsumsq / dmatch1);
	ysig = sqrt (ysumsq / dmatch1);
	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
	siglim = 2.0 * rsig;
	if (debug) {
	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
		    xmean, xsig, ymean, ysig, rmean, rsig);
	    }

	/* sort by increasing total residual */
	for (i = 0; i < nmatch-1; i++) {
	    for (j = i+1; j < nmatch; j++) {
		if (resid[j] < resid[i]) {
		    double tmp;

		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
		    }
		}
	    }

	/* Cut off points at residual of two sigma */
	for (i = 0; i < nmatch; i++) {
	    if (resid[i] > siglim) {
		if (i > bestbin) bestbin = i - 1;
		break;
		}
	    }

	xref_p = vimoswcs->xref;
	yref_p = vimoswcs->yref;
	xrefpix = vimoswcs->xrefpix;
	yrefpix = vimoswcs->yrefpix;
	nbin_p = bestbin;
	vimoswcs_amoeba (vimoswcs);

	if (debug) {
	    ra2str (rastr, 16, vimoswcs->xref, 3);
	    dec2str (decstr, 16, vimoswcs->yref, 2);
	    fprintf (stderr,"\nresid solution:\n");
	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
			    bestbin, siglim);
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		 rastr, decstr, 3600.0*vimoswcs->xinc, 3600.0*vimoswcs->yinc, vimoswcs->rot);
	    fprintf (stderr,"(%8.2f,%8.2f)\n", vimoswcs->xrefpix, vimoswcs->yrefpix);
	    }
	free (resid);
	free (xe);
	free (ye);
	}

    free (sbx);
    free (sby);
    free (gbra);
    free (gbdec);
    free (is);
    free (ig);
    free (ibs);
    free (ibg);

    return (nmatch);
}

int
ReadMatch (filename, sx, sy, sra, sdec, debug)

char	*filename;	/* Name of file containing matches */
double	**sx;		/* Image star X coordinates in pixels */
double	**sy;		/* Image star Y coordinates in pixels */
double	**sra;		/* Probable image star right ascensions in degrees */
double	**sdec;		/* Probable image star declinations in degrees */
int	debug;		/* Printed debugging information if not zero */

{
    int nbytes, nread, ir, ntok, itok, iytok;
    double *tx, *ty, *tra, *tdec, ra, dec, x, y;
    int ndec;
    int nmatch = 0;	/* Number of matches read from file */
    char rastr[32], decstr[32];

    /* If tab file, read from ra, dec, x, y  columns */
    if (istab (filename)) {
	}

    /* Otherwise, assume first 4 columns are x, y, ra, dec */
    else {
	char line[1025];
	char *nextline, *lastchar;
	FILE *fd;
	struct Tokens tokens;	/* Token structure */
	char *cwhite;		/* additional whitespace characters */
	char token[256];

	cwhite = NULL;

	/* Open input file */
	if (!strcmp (filename, "stdin")) {
	    fd = stdin;
	    nread = 1000;
	    }
	else {
	    nread = getfilelines (filename);
	    if (!(fd = fopen (filename, "r"))) {
	    fprintf (stderr, "SetVIMOSWCSFITS: Match file %s could not be opened\n",
		     filename);
		return (0);
		}
	    }
	nbytes = nread * sizeof (double);
	if (!(tra = (double *) calloc (nread, sizeof(double))))
	    fprintf (stderr, "Could not calloc %d bytes for gra\n", nbytes);
	if (!(tdec = (double *) calloc (nread, sizeof(double))))
	    fprintf (stderr, "Could not calloc %d bytes for gdec\n", nbytes);
	if (!(tx = (double *) calloc (nread, sizeof(double))))
	    fprintf (stderr, "Could not calloc %d bytes for sx\n", nbytes);
	if (!(ty = (double *) calloc (nread, sizeof(double))))
	    fprintf (stderr, "Could not calloc %d bytes for sy\n", nbytes);
	*sra = tra;
	*sdec = tdec;
	*sx = tx;
	*sy = ty;

	nmatch = 0;
	nextline = line;
        for (ir = 0; ir < nread; ir++) {
	    if (fgets (line, 1024, fd) == NULL)
		break;

	    /* Skip lines with comments */
	    if (line[0] == '#')
		continue;

	    /* Drop linefeeds */
	    lastchar = nextline + strlen(nextline) - 1;
	    if (*lastchar < 32)
		*lastchar = (char) 0;

	    /* Read X, Y, RA, and Dec from each line,
		skipping line if all four are not present and numbers */
	    ntok = setoken (&tokens, line, cwhite);
	    if (ntok < 1)
		break;
	    if (ntok < 4)
		continue;

	    /* if (debug)
		fprintf (stderr, "%d: %s\n", nmatch, line); */

	    /* Image X coordinate or RA */
	    itok = 1;
	    if (getoken(&tokens, itok, token)) {

		/* Read RA, Dec, X, Y if first token has : in it */
		if (strchr (token, ':') != NULL) {
		    ra = str2ra (token);
		    iytok = 4;
		    if (getoken(&tokens, 2, token))
			dec = str2dec (token);
		    if (getoken(&tokens, 3, token)) {
			if (isnum (token))
			    x = atof (token);
			else {
			    iytok = 5;
			    if (getoken(&tokens, 4, token)) {
				if (isnum (token))
				    x = atof (token);
				else
				    continue;
				}
			    }
			}
		    if (getoken(&tokens, iytok, token)) {
			if (isnum (token))
			    y = atof (token);
			else
			    continue;
			}
		    tx[nmatch] = x;
		    ty[nmatch] = y;
		    tra[nmatch] = ra;
		    tdec[nmatch] = dec;
		    nmatch++;
		    continue;
		    }
		if (isnum (token))
		    x = atof (token);
		else
		    continue;
		}
	    else
		continue;

	    /* Image Y coordinate */
	    itok++;
	    if (getoken(&tokens, itok, token)) {
		if (isnum (token))
		    y = atof (token);
		else
		    continue;
		}
	    else
		continue;

	    /* Right ascension */
	    itok++;
	    if (getoken(&tokens, itok, token)) {
		if (isnum (token) == 1) {
		    ra = atof (token);
		    itok++;
		    if (getoken(&tokens, itok, token)) {
			if (isnum (token) == 2)
			    ra = ra + (atof (token) / 60.0);
			else if (isnum (token) == 1) {
			    ra = ra + (atof (token) / 60.0);
			    itok++;
			    if (getoken(&tokens, itok, token)) {
				if (isnum (token))
				    ra = ra + (atof (token) / 3600.0);
				}
			    }
			}
		    ra = ra * 15.0;
		    }
		else
		    ra = str2ra (token);
		}
	    else
		continue;

	    /* Declination */
	    itok++;
	    if (getoken(&tokens, itok, token)) {
		if (isnum (token) == 1) {
		    dec = atof (token);
		    itok++;
		    if (strchr (token, '-') != NULL)
			ndec = 1;
		    else
			ndec = 0;
		    if (getoken(&tokens, itok, token)) {
			if (isnum (token) == 2) {
			    if (ndec)
				dec = dec - (atof (token) / 60.0);
			    else
				dec = dec + (atof (token) / 60.0);
			    }
			else if (isnum (token) == 1) {
			    if (ndec)
				dec = dec - (atof (token) / 60.0);
			    else
				dec = dec + (atof (token) / 60.0);
			    itok++;
			    if (getoken(&tokens, itok, token)) {
				if (isnum (token)) {
				    if (ndec)
					dec = dec - (atof (token) / 3600.0);
				    else
					dec = dec + (atof (token) / 3600.0);
				    }
				}
			    }
			}
		    }
		else
		    dec = str2dec (token);
		}
	    else
		continue;
	    tx[nmatch] = x;
	    ty[nmatch] = y;
	    tra[nmatch] = ra;
	    tdec[nmatch] = dec;
	    if (debug) {
		ra2str (rastr, 32, tra[nmatch], 3);
		dec2str (decstr, 32, tdec[nmatch], 2);
		fprintf (stderr, "%d: %8.3f %8.3f %s %s\n", nmatch,
			 tx[nmatch], ty[nmatch], rastr, decstr);
		}
	    nmatch++;
	    }
	}

    return (nmatch);
}


/* Find shift, scale, and rotation of image stars to best-match reference stars
 * Return count of total coincidences found, else 0 if none or -1 if trouble.
 */

int
FitMatch (nmatch, sbx, sby, gbra, gbdec, vimoswcs, debug)

int	nmatch;		/* Number of matched stars */
double	*sbx;		/* Image star X coordinates in pixels */
double	*sby;		/* Image star Y coordinates in pixels */
double	*gbra;		/* Reference star right ascensions in degrees */
double	*gbdec;		/* Reference star right ascensions in degrees */
struct WorldCoor *vimoswcs;	/* World coordinate structure (fit returned) */
int	debug;		/* Printed debugging information if not zero */

{
    int i;
    char rastr[16], decstr[16];
    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
    int bestbin;	/* Number of coincidences for refit */
    int pfit;		/* List of parameters to fit, 1 per digit */
    char vpar[16];	/* List of parameters to fit */
    double xdiff, ydiff;
    char *vi;
    char vc;
    int offscl;
    int nsc, j;
    double equinox = vimoswcs->equinox;
    double tx = 0.0;
    double ty = 0.0;
    double tra = 0.0;
    double tdec = 0.0;
    double tdiff = 0.0;
    double cra, cdec, cx, cy, scale;
    double dmatch;
    double skydiff, imdiff;

    dmatch = (double) nmatch;

    if (debug) {
	fprintf (stderr,"%d matched stars:\n", nmatch);
	}

    /* too few hits */
    if (nmatch < minbin)
	return (nmatch);

    /* Compute plate scale and center of stars */
    nsc = 0;
    for (i = 0; i < nmatch; i++) {
	tx = tx + sbx[i];
	ty = ty + sby[i];
	tra = tra + gbra[i];
	tdec = tdec + gbdec[i];
	for (j = i+1; j < nmatch; j++) {
	    skydiff = vimoswcsdist (gbra[i], gbdec[i], gbra[j], gbdec[j]);
	    xdiff = sbx[j] - sbx[i];
	    ydiff = sby[j] - sby[i];
	    imdiff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
	    scale = skydiff / imdiff;
	    tdiff = tdiff + scale;
	    nsc++;
	    if (debug) {
		fprintf (stderr,"%d %d: sky: %8g, image: %8g, %8g deg/pix", 
			i, j, skydiff, imdiff, scale);
		fprintf (stderr," = %8g arcsec/pix\n", scale * 3600.0);
		}
	    }
	}
    
    /* Reset image center in WCS data structure based on star matching */
    /* cra = tra / dmatch;
    cdec = tdec / dmatch;
    cx = tx / dmatch;
    cy = ty / dmatch;
    scale = tdiff / (double) nsc;
    if (debug)
	fprintf (stderr,"scale = %8g deg/pix = %8g arcsec/pix\n", scale, scale*3600.0);
    vimoswcsreset (vimoswcs, cx, cy, cra, cdec, scale, 0.0, 0.0, NULL, equinox); */

    /* Provide non-parametric access to the star lists */
    sx_p = sbx;
    sy_p = sby;
    gra_p = gbra;
    gdec_p = gbdec;
    xref_p = vimoswcs->xref;
    yref_p = vimoswcs->yref;
    xrefpix = vimoswcs->xrefpix;
    yrefpix = vimoswcs->yrefpix;
    nbin_p = nmatch;

    /* Number of parameters to fit from command line or number of matches */
    if (pfit0 != 0) {
	if (pfit0 < 3)
	    pfit = 12;
	else if (pfit0 == 3)	/* Fit center and plate scale */
	    pfit = 123;
	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
	    pfit = 1235;
	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
	    pfit = 12345;
	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
	    pfit = 123456;
	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
	    pfit = 1234578;
	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
	    pfit = 12345678;
	else
	    pfit = pfit0;
	}
    else if (nmatch < 4)
	pfit = 12;
    else if (nmatch < 6)
	pfit = 123;
    else
	pfit = 12345;

    /* Get parameters to fit from digits of pfit */
    sprintf (vpar, "%d", pfit);
    nfit = 0;
    vfit[0] = -1;
    for (i = 1; i < NPAR1; i++) {
	vc = i + 48;
	vi = strchr (vpar, vc);
	if (vi != NULL) {
	    vfit[i] = vi - vpar;
	    nfit++;
	    }
	else
	    vfit[i] = -1;
	}

    /* Set initial guesses for parameters which are being fit */
    xref0 = vimoswcs->xref;
    yref0 = vimoswcs->yref;
    xinc0 = vimoswcs->xinc;
    yinc0 = vimoswcs->yinc;
    rot0 = vimoswcs->rot;
    xrefpix0 = vimoswcs->xrefpix;
    yrefpix0 = vimoswcs->yrefpix;
    cd0[0] = vimoswcs->cd[0];
    cd0[1] = vimoswcs->cd[1];
    cd0[2] = vimoswcs->cd[2];
    cd0[3] = vimoswcs->cd[3];
    if (vfit[6] > -1)
	cdfit = 1;
    else
	cdfit = 0;

    /* Fit image star coordinates to reference star positions */
    vimoswcs_amoeba (vimoswcs);

    if (debug) {
	fprintf (stderr,"\nAmoeba fit:\n");
	ra2str (rastr, 16, xref0, 3);
	dec2str (decstr, 16, yref0, 2);
	fprintf (stderr,"   initial guess:\n");
	if (vfit[6] > -1)
	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
	else
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);

	ra2str (rastr, 16, vimoswcs->xref, 3);
	dec2str (decstr, 16, vimoswcs->yref, 2);
	fprintf (stderr,"\nfirst solution:\n");
	if (vfit[6] > -1)
	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
		     rastr,decstr,vimoswcs->cd[0],vimoswcs->cd[1],vimoswcs->cd[2],vimoswcs->cd[3]);
	else
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		     rastr,decstr,3600.0*vimoswcs->xinc,3600.0*vimoswcs->yinc,vimoswcs->rot);
	fprintf (stderr,"(%8.2f,%8.2f)\n", vimoswcs->xrefpix, vimoswcs->yrefpix);
	}

    /* If we have extra bins, repeat with the best ones */
    bestbin = nfit + 1;
    if (resid_refine && nmatch > bestbin) {
	double *resid = (double *) malloc (nmatch * sizeof(double));
	double *xe = (double *) malloc (nmatch * sizeof(double));
	double *ye = (double *) malloc (nmatch * sizeof(double));
	int i, j;
	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
	double mra, mdec, xsig, ysig, rsig, siglim;
	char vimoswcstring[64];
	double xsum = 0.0;
	double ysum = 0.0;
	double rsum = 0.0;
	double dmatch = (double)nmatch;
	double dmatch1 = (double)(nmatch - 1);

	/* Compute residuals at each star location */
	for (i = 0; i < nmatch; i++) {
	    pix2vimoswcs (vimoswcs, sbx[i], sby[i], &mra, &mdec);
	    xe[i] = (mra - gbra[i]) * 3600.0;
	    ye[i] = (mdec - gbdec[i]) * 3600.0;
	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
	    if (debug) {
		pix2vimoswcst (vimoswcs, sbx[i], sby[i], vimoswcstring, 64);
		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
		    i, sbx[i], sby[i], vimoswcstring, xe[i], ye[i], resid[i]);
		}
	    xsum = xsum + xe[i];
	    ysum = ysum + ye[i];
	    rsum = rsum + resid[i];
	    }

	/* Compute means and standard deviations */
	xmean = xsum / dmatch;
	ymean = ysum / dmatch;
	rmean = rsum / dmatch;
	xsumsq = 0.0;
	ysumsq = 0.0;
	for (i = 0; i < nmatch; i++) {
	    diff = xe[i] - xmean;
	    xsumsq = xsumsq + (diff * diff);
	    diff = ye[i] - ymean;
	    ysumsq = ysumsq + (diff * diff);
	    }
	xsig = sqrt (xsumsq / dmatch1);
	ysig = sqrt (ysumsq / dmatch1);
	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
	siglim = 2.0 * rsig;
	if (debug) {
	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
		    xmean, xsig, ymean, ysig, rmean, rsig);
	    }

	/* sort by increasing total residual */
	for (i = 0; i < nmatch-1; i++) {
	    for (j = i+1; j < nmatch; j++) {
		if (resid[j] < resid[i]) {
		    double tmp;

		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
		    }
		}
	    }

	/* Cut off points at residual of two sigma */
	for (i = 0; i < nmatch; i++) {
	    if (resid[i] > siglim) {
		if (i > bestbin) bestbin = i - 1;
		break;
		}
	    }

	xref_p = vimoswcs->xref;
	yref_p = vimoswcs->yref;
	xrefpix = vimoswcs->xrefpix;
	yrefpix = vimoswcs->yrefpix;
	nbin_p = bestbin;
	vimoswcs_amoeba (vimoswcs);

	if (debug) {
	    ra2str (rastr, 16, vimoswcs->xref, 3);
	    dec2str (decstr, 16, vimoswcs->yref, 2);
	    fprintf (stderr,"\nresid solution:\n");
	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
			    bestbin, siglim);
	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
		 rastr, decstr, 3600.0*vimoswcs->xinc, 3600.0*vimoswcs->yinc, vimoswcs->rot);
	    fprintf (stderr,"(%8.2f,%8.2f)\n", vimoswcs->xrefpix, vimoswcs->yrefpix);
	    }
	free (resid);
	free (xe);
	free (ye);
	}

    return (nmatch);
}

struct WorldCoor *vimoswcsf;

static double vimoswcs_chisqr ();

/* From Numerical Recipes */
void amoeba();
static double amotry();


/* Set up the necessary temp arrays and call the amoeba() multivariate solver */

static void
vimoswcs_amoeba (vimoswcs0)

struct WorldCoor *vimoswcs0;

{
    double *p[NPAR1];				  /* used as p[NPAR1][NPAR] */
    double vguess[NPAR], vp[NPAR], vdiff[NPAR];
    double p0[NPAR], p1[NPAR], p2[NPAR], p3[NPAR], p4[NPAR],
	   p5[NPAR], p6[NPAR], p7[NPAR], p8[NPAR]; /* used as px[0..NPAR-1] */
    double y[NPAR1];				  /* used as y[1..NPAR] */
    double xinc1, yinc1, xrefpix1, yrefpix1, rot, cd[4];
    double sumx, sumy, sumr;
    int iter;
    int i, j;
    int nfit1;
    char rastr[16],decstr[16];
    int nitmax;

    nitmax = NMAX;
    if (nfit > NPAR)
	nfit = NPAR;
    nfit1 = nfit + 1;
    vimoswcsf = vimoswcs0;

/* Initialize guess and difference vectors to zero */
    for (i = 0; i < NPAR; i++) {
	vguess[i] = 0.0;
	vdiff[i] = 0.0;
	}

    /* Optical axis center (RA and Dec degrees) */
    if (vfit[1] > -1) {
	vguess[vfit[1]] = 0.0;
	vdiff[vfit[1]] = 5.0 * vimoswcsf->xinc;
	}
    if (vfit[2] > -1) {
	vguess[vfit[2]] = 0.0;
	vdiff[vfit[2]] = 5.0 * vimoswcsf->yinc;
	}
    /* Second rotation about optical axis (degrees) -> CD matrix */
    if (vfit[6] > -1) {
	vimoswcsf->rotmat = 1;
	vguess[vfit[3]] = vimoswcsf->cd[0];
	vdiff[vfit[3]] = vimoswcsf->xinc * 0.03;
	vguess[vfit[4]] = vimoswcsf->cd[1];
	vdiff[vfit[4]] = vimoswcsf->yinc * 0.03;
	vguess[vfit[5]] = vimoswcsf->cd[2];
	vdiff[vfit[5]] = vimoswcsf->xinc * 0.03;
	vguess[vfit[6]] = vimoswcsf->cd[3];
	vdiff[vfit[6]] = vimoswcsf->yinc * 0.03;
	}

    else {

    /* Plate scale at optical axis right ascension or both (degrees/pixel) */
	if (vfit[3] > -1) {
	    vguess[vfit[3]] = vimoswcsf->xinc;
	    vdiff[vfit[3]] = vimoswcsf->xinc * 0.03;
	    }

    /* Plate scale in declination at optical axis (degrees/pixel) */
	if (vfit[4] > -1) {
	    vguess[vfit[4]] = vimoswcsf->yinc;
	    vdiff[vfit[4]] = vimoswcsf->yinc * 0.03;
	    }

    /* Rotation about optical axis in degrees */
	if (vfit[5] > -1) {
	    vguess[vfit[5]] = vimoswcsf->rot;
	    vdiff[vfit[5]] = 0.5;
	    }
	}

/* Reference pixel (optical axis) */
    if (vfit[7] > -1) {
	vguess[vfit[7]] = 0.0;
	vdiff[vfit[7]] = 10.0;
	}
    if (vfit[8] > -1) {
	vguess[vfit[8]] = 0.0;
	vdiff[vfit[8]] = 10.0;
	}

/* Set up matrix of nfit+1 initial guesses.
 * The supplied guess, plus one for each parameter altered by a small amount
 */
    p[0] = p0;
    if (nfit > 0) p[1] = p1;
    if (nfit > 1) p[2] = p2;
    if (nfit > 2) p[3] = p3;
    if (nfit > 3) p[4] = p4;
    if (nfit > 4) p[5] = p5;
    if (nfit > 5) p[6] = p6;
    if (nfit > 6) p[7] = p7;
    if (nfit > 7) p[8] = p8;
    for (i = 0; i <= nfit; i++) {
	for (j = 0; j < nfit; j++)
	    p[i][j] = vguess[j];
	if (i > 0 && i <= nfit)
	    p[i][i-1] = vguess[i-1] + vdiff[i-1];
	 y[i] = vimoswcs_chisqr (p[i], -i);
	}

#ifdef	PDUMP
    fprintf (stderr,"Before:\n");
    for (i = 0; i < nfit1; i++) {
	if (vfit[1] > -1)
	    ra2str (rastr, 16, p[i][vfit[1]] + xref_p, 3);
	else
	    ra2str (rastr, 16, vimoswcsf->xref, 3);
	if (vfit[2] > -1)
	    dec2str (decstr, 16, p[i][vfit[2]]+yref_p, 2);
	else
	    dec2str (decstr, 16, vimoswcsf->yref, 2);
	if (vfit[6] > -1) {
	    cd[0] = p[i][vfit[3]];
	    cd[1] = p[i][vfit[4]];
	    cd[2] = p[i][vfit[5]];
	    cd[3] = p[i][vfit[6]];
	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
	    }
	else {
	    if (vfit[3] > -1)
		xinc1 = p[i][vfit[3]];
	    else
		xinc1 = vimoswcsf->xinc;
	    if (vfit[4] > -1)
		yinc1 = p[i][vfit[4]];
	    else if (vfit[3] > -1) {
		if (xinc1 < 0)
		    yinc1 = -xinc1;
		else
		    yinc1 = xinc1;
		}
	    else
		yinc1 = vimoswcsf->yinc;
	    if (vfit[5] > -1)
		rot = p[i][vfit[5]];
	    else
		rot = vimoswcsf->rot;
	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
		    i, rastr, decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
	    }

	if (vfit[7] > -1)
	    xrefpix1 = xrefpix + p[i][vfit[7]];
	else
	    xrefpix1 = vimoswcsf->xrefpix;
	if (vfit[8] > -1)
	    yrefpix1 = yrefpix + p[i][vfit[8]];
	else
	    yrefpix1 = vimoswcsf->yrefpix;
	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
	}
#endif

    amoeba (p, y, nfit, FTOL, nitmax, vimoswcs_chisqr, &iter);

#ifdef	PDUMP
    fprintf (stderr,"\nAfter:\n");
    for (i = 0; i < nfit1; i++) {
	if (vfit[1] > -1)
	    ra2str (rastr, 16, p[i][vfit[1]] + xref_p, 3);
	else
	    ra2str (rastr, 16, vimoswcsf->xref, 3);
	if (vfit[2] > -1)
	    dec2str (decstr, 16, p[i][vfit[2]]+yref_p, 2);
	else
	    dec2str (decstr, 16, vimoswcsf->yref, 2);
	if (vfit[6] > -1) {
	    cd[0] = p[i][vfit[3]];
	    cd[1] = p[i][vfit[4]];
	    cd[2] = p[i][vfit[5]];
	    cd[3] = p[i][vfit[6]];
	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
	    }
	else {
	    if (vfit[3] > -1)
		xinc1 = p[i][vfit[3]];
	    else
		xinc1 = vimoswcsf->xinc;
	    if (vfit[4] > -1)
		yinc1 = p[i][vfit[4]];
	    else if (vfit[3] > -1) {
		if (xinc1 < 0)
		    yinc1 = -xinc1;
		else
		    yinc1 = xinc1;
		}
	    else
		yinc1 = vimoswcsf->yinc;
	    if (vfit[5] > -1)
		rot = p[i][vfit[5]];
	    else
		rot = vimoswcsf->rot;
	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
		    i,rastr,decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
	    }
	if (vfit[7] > -1)
	    xrefpix1 = xrefpix + p[i][vfit[7]];
	else
	    xrefpix1 = vimoswcsf->xrefpix;
	if (vfit[8] > -1)
	    yrefpix1 = yrefpix + p[i][vfit[8]];
	else
	    yrefpix1 = vimoswcsf->yrefpix;
	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
	}
#endif

    /* On return, all entries in p[1..NPAR] are within FTOL;
     * Return the average, though you could just pick the first one
     */
    for (j = 0; j < nfit; j++) {
	double sum = 0.0;
	for (i = 0; i < nfit1; i++)
	    sum += p[i][j];
	vp[j] = sum / (double)nfit1;
	}
    if (vfit[1] > -1)
	vimoswcsf->xref = xref_p + vp[vfit[1]];
    if (vfit[2] > -1)
	vimoswcsf->yref = yref_p + vp[vfit[2]];
    if (vfit[6] > -1) {
	vimoswcsf->cd[0] = vp[vfit[3]];
	vimoswcsf->cd[1] = vp[vfit[4]];
	vimoswcsf->cd[2] = vp[vfit[5]];
	vimoswcsf->cd[3] = vp[vfit[6]];
	}
    else {
	if (vfit[3] > -1)
	    vimoswcsf->xinc = vp[vfit[3]];
	if (vfit[4] > -1)
	    vimoswcsf->yinc = vp[vfit[4]];
	else if (vfit[3] > -1) {
	    if (vimoswcsf->xinc < 0)
		vimoswcsf->yinc = -vimoswcsf->xinc;
	    else
		vimoswcsf->yinc = vimoswcsf->xinc;
	    }
	if (vfit[5] > -1)
	    vimoswcsf->rot = vp[vfit[5]];
	}
    if (vfit[7] > -1)
	vimoswcsf->xrefpix = xrefpix + vp[vfit[7]];
    if (vfit[8] > -1)
	vimoswcsf->yrefpix = yrefpix + vp[vfit[8]];

#ifdef RESIDDUMP
    ra2str (rastr, 16, vimoswcsf->xref, 3);
    dec2str (decstr, 16, vimoswcsf->yref, 2);

    if (vfit[6] > -1)
	fprintf (stderr,"iter=%d\n cra= %s cdec= %s CD=%9.7f,%9.7f,%9.7f,%9.7f ", iter,
		rastr, decstr, vimoswcsf->cd[0], vimoswcsf->cd[1], vimoswcsf->cd[2],
		vimoswcsf->cd[3]);
    else
	fprintf (stderr,"iter=%d\n cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ", iter,
		rastr, decstr, vimoswcsf->xinc*3600.0, vimoswcsf->yinc*3600.0, vimoswcsf->rot);
    fprintf (stderr,"(%8.2f,%8.2f)\n", vimoswcsf->xrefpix, vimoswcsf->yrefpix);
    sumx = 0.0;
    sumy = 0.0;
    sumr = 0.0;
    for (i = 0; i < nbin_p; i++) {
	double mra, mdec, ex, ey, er;
	char rastr[16], decstr[16];

	pix2vimoswcs (vimoswcsf, sx_p[i], sy_p[i], &mra, &mdec);
	ex = 3600.0 * (mra - gra_p[i]);
	ey = 3600.0 * (mdec - gdec_p[i]);
	er = sqrt (ex * ex + ey * ey);
	sumx = sumx + ex;
	sumy = sumy + ey;
	sumr = sumr + er;

	ra2str (rastr, 16, gra_p[i], 3);
	dec2str (decstr, 16, gdec_p[i], 2);
	fprintf (stderr,"%2d: c: %s %s ", i+1, rastr, decstr);
	ra2str (rastr, 16, mra, 3);
	dec2str (decstr, 16, mdec, 2);
	fprintf (stderr, "i: %s %s %6.3f %6.3f %6.3f\n",
		rastr, decstr, 3600.0*ex, 3600.0*ey,
		3600.0*sqrt(ex*ex + ey*ey));
	}
    sumx = sumx / (double)nbin_p;
    sumy = sumy / (double)nbin_p;
    sumr = sumr / (double)nbin_p;
    fprintf (stderr,"mean dra: %6.3f, ddec: %6.3f, dr = %6.3f\n", sumx, sumy, sumr);
#endif
}


/* Compute the chisqr of the vector v, where
 * v[0]=cra, v[1]=cdec, v[2]=ra deg/pix, v[3]=dec deg/pix,
 * v[4]=rotation, v[5]=2nd rotation->CD matrix, v[6]=ref x, and v[7] = ref y
 * chisqr is in arcsec^2
 */

static double
vimoswcs_chisqr (v, iter)

double	*v;	/* Vector of parameter values */
int	iter;	/* Number of iterations */

{
    double chsq;
    char rastr[16],decstr[16];
    double xmp, ymp, dx, dy, cd[4], *cdx;
    double crval1, crval2, cdelt1, cdelt2, crota, crpix1, crpix2;
    int i, offscale;

    /* Set WCS parameters from fit parameter vector */

    /* Sky coordinates at optical axis (degrees) */
    if (vfit[1] > -1)
	crval1 = xref_p + v[vfit[1]];
    else
	crval1 = vimoswcsf->xref;
    if (vfit[2] > -1)
	crval2 = yref_p + v[vfit[2]];
    else
	crval2 = vimoswcsf->yref;

    /* CD matrix */
    if (vfit[6] > -1) {
	cdelt1 = 0.0;
	cdelt2 = 0.0;
	crota = 0.0;
	cd[0] = v[vfit[3]];
	cd[1] = v[vfit[4]];
	cd[2] = v[vfit[5]];
	cd[3] = v[vfit[6]];
	cdx = cd;
	}

    else {
	/* Plate scale (degrees/pixel) */
	if (vfit[3] > -1)
	    cdelt1 = v[vfit[3]];
	else
	    cdelt1 = vimoswcsf->xinc;
	if (vfit[4] > -1)
	    cdelt2 = v[vfit[4]];
	else if (vfit[3] > -1) {
	    if (cdelt1 < 0)
		cdelt2 = -cdelt1;
	    else
		cdelt2 = cdelt1;
	    }
	else
	    cdelt2 = vimoswcsf->yinc;

	/* Rotation angle (degrees) */
	if (vfit[5] > -1)
	    crota = v[vfit[5]];
	else
	    crota = vimoswcsf->rot;
	cdx = NULL;
	}

    /* Optical axis pixel coordinates */
    if (vfit[7] > -1)
	crpix1 = xrefpix + v[vfit[7]];
    else
	crpix1 = vimoswcsf->xrefpix;
    if (vfit[8] > -1)
	crpix2 = yrefpix + v[vfit[8]];
    else
	crpix2 = vimoswcsf->yrefpix;
    if (vimoswcsreset (vimoswcsf,crpix1,crpix2,crval1,crval2,cdelt1,cdelt2,crota,cdx)) {
	fprintf (stderr,"CHISQR: Cannot reset WCS!\n");
	return (0.0);
	}

    /* Compute sum of squared residuals for these parameters */
    chsq = 0.0;
    for (i = 0; i < nbin_p; i++) {
	vimoswcs2pix (vimoswcsf, gra_p[i], gdec_p[i], &xmp, &ymp, &offscale);
	/* if (!offscale) { */
	    dx = xmp - sx_p[i];
	    dy = ymp - sy_p[i];
	    chsq += dx*dx + dy*dy;
	    /* } */
	}

#ifdef TRACE_CHSQR
    ra2str (rastr, 16, vimoswcsf->xref, 3);
    dec2str (decstr, 16, vimoswcsf->yref, 2);
    if (vfit[6] > -1)
	fprintf (stderr,"%4d: %s %s CD: %9.7f,%9.7f,%9.7f,%9.7f ",
		iter, rastr, decstr, vimoswcsf->cd[0],vimoswcsf->cd[1],vimoswcsf->cd[2],
		vimoswcsf->cd[3]);
    else
	fprintf (stderr,"%4d: %s %s %9.7f,%9.7f %8.5f ",
		iter, rastr, decstr, vimoswcsf->xinc*3600.0, vimoswcsf->yinc*3600.0,
		vimoswcsf->rot);
    fprintf (stderr,"(%8.2f,%8.2f) -> %f\r",
	     vimoswcsf->xrefpix, vimoswcsf->yrefpix, chsq);
#endif
    return (chsq);
}

/* The following subroutines are based on those in Numerical Recipes in C */

/* amoeba.c */

#define ALPHA 1.0
#define BETA 0.5
#define GAMMA 2.0

void
amoeba (p, y, ndim, ftol, itmax, funk, nfunk)

double	**p;
double	y[];
double	ftol;
int	itmax;
double	(*funk)();
int	ndim;
int	*nfunk;

{
    int i,j,ilo,ihi,inhi,ndim1=ndim+1;
    double ytry,ysave,sum,rtol,*psum;

    psum = (double *) malloc ((unsigned)ndim * sizeof(double));
    *nfunk = 0;
    for (j=0; j<ndim; j++) {
        for (i=0,sum=0.0; i<ndim1; i++)
            sum += p[i][j]; psum[j]=sum;
    }
    for (;;) {
        ilo=1;
        if (y[0] > y[1]) {
            inhi = 1;
            ihi = 0;
        }
        else {
            inhi = 0;
            ihi = 1;
        }
        for (i = 0; i < ndim1; i++) {
            if (y[i] < y[ilo])
                ilo=i;
            if (y[i] > y[ihi]) {
                inhi=ihi;
                ihi=i;
            }
            else if (y[i] > y[inhi])
                if (i != ihi)
                    inhi=i;
        }
        rtol = 2.0 * fabs(y[ihi]-y[ilo]) / (fabs(y[ihi]) + fabs(y[ilo]));
        if (rtol < ftol)
            break;
        if (*nfunk >= itmax) {
            fprintf (stderr,"Numerical Recipes run-time error...\n");
            fprintf (stderr,"Too many iterations in AMOEBA %d > %d",*nfunk,itmax);
            return;
        }
        ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, -ALPHA);
        if (ytry <= y[ilo]) {
            ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, GAMMA);
        }
        else if (ytry >= y[inhi]) {
            ysave = y[ihi];
            ytry = amotry (p,y,psum,ndim,funk,ihi,nfunk,BETA);
            if (ytry >= ysave) {
                for (i = 0; i < ndim1; i++) {
                    if (i != ilo) {
                        for (j = 0; j < ndim; j++) {
                            psum[j] = 0.5 * (p[i][j] + p[ilo][j]);
                            p[i][j] = psum[j];
                        }
                        y[i]=(*funk)(psum, *nfunk);
                    }
                }
                *nfunk += ndim;
                for (j=0; j<ndim; j++) {
                    for (i=0,sum=0.0; i<ndim1; i++)
                        sum += p[i][j]; psum[j]=sum;
                }
            }
        }
    }
    free (psum);
    return;
}


static double
amotry (p, y, psum, ndim, funk, ihi, nfunk, fac)

double	**p;
double	*y;
double	*psum;
double	(*funk)();
double	fac;
int	ndim;
int	ihi;
int	*nfunk;

{
    int j;
    double fac1,fac2,ytry,*ptry;

    ptry = (double *) malloc ((unsigned) ndim * sizeof(double));
    fac1 = (1.0 - fac) / ndim;
    fac2 = fac1 - fac;
    for (j = 0; j < ndim; j++)
	ptry[j] = psum[j] * fac1 - p[ihi][j] * fac2;
    ytry = (*funk)(ptry, *nfunk);
    ++(*nfunk);
    if (ytry < y[ihi]) {
	y[ihi] = ytry;
	for (j = 0; j < ndim; j++) {
    	    psum[j] +=  ptry[j] - p[ihi][j];
    	    p[ihi][j] = ptry[j];
	    }
	}
    free (ptry);
    return ytry;
}

void
setresid_refine (refine)
int refine;
{ resid_refine = refine; return; }

int
getresid_refine ()
{ return (resid_refine); }

void
setnfit (nfit)
int nfit;
{
    if (nfit == 0)
	setnofit();
    else if (nfit < 0) {
	pfit0 = -nfit;
	resid_refine = 1;
	}
    else {
	pfit0 = nfit;
	resid_refine = 0;
	}
    return;
}

int
iscdfit ()
{ return (cdfit); }

/* Aug  6 1996	New subroutine
 * Sep  1 1996	Move constants to lvimoswcs.h
 * Sep  3 1996	Use offscale pixels for chi^2 computation
 * Sep  3 1996	Overprint chi^2 in verbose mode
 * Oct 15 1996	Fix am* subroutine declarations
 * Nov 19 1996	Fix bug regarding rotation
 *
 * Jul 21 1997	Add reference pixel position fitting
 * Aug  4 1997	Increase maximum iterations from 750 to 1000 in lvimoswcs.h
 * Aug 28 1997	Fix VGUESS dimension bug
 * Sep  9 1997	Print RA and Dec offsets in residual listing
 * Sep  9 1997	Turn on resid_refinement if number of parameters to fit negated
 * Sep  9 1997	Fit separate horizontal and vertical plate scales if nfit=5
 * Sep  9 1997	Fix bugs associated with fitting optical axis
 * Sep 12 1997	Add chip rotation instead of second plate scale
 * Oct  2 1997	Keep second plate scale AND chip rotation
 * Oct 16 1997	Try to deal with reference pixel position correctly
 * Nov  5 1997	Select parameters one at a time, in any order
 * Nov 12 1997	Add PFIT=3 to fit center and plate scale only
 * Dec 15 1997	Fix minor bugs after lint
 *
 * Jan 26 1998	Remove chip rotation code
 * Jan 29 1998	Streamline initialization code
 * Feb 19 1998	Fix bug in initialization code
 * Mar  3 1998	Fix residual-refining code
 * Mar 20 1998	Add option to fit CD matrix
 * Mar 25 1998	Make amoeba() externally callable
 * Mar 26 1998	Return instead of crashing when too many iterations
 * Apr 21 1998	Drop out of loop if more than half of stars are matched
 * Apr 27 1998	Fix bug handling nfit=8
 * Jun 24 1998	Fix bug summing unitialized values for mean after fit
 * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
 * Oct  8 1998	Initialize bestdx and bestdy to zero
 * Dec  8 1998	Fix declaration of amotry()
 *
 * Apr 21 1999	Add subroutines to set and retrieve resid_refine independently
 * Jul 21 1999	Add FitMatch() to fit WCS to already-matched stars
 * Sep  8 1999	Fix bug found by Jean-Baptiste Marquette
 * Oct  1 1999	Add ReadMatch() to read a set of matches from a file
 * Oct 20 1999	Include vimoswcscat.h
 *
 * Feb 15 2000	Add iscdfit() to return wheterh CD matrix is being fit
 * Mar 10 2000	Add debug statement to list max matches as they are found
 * Mar 10 2000	Change loop order to image stars first
 * Dec 18 2000	Write half of ReadMatch() to deal with ASCII files
 *
 * Jan  2 2001	Modify ReadMatch() to read hh mm ss dd mm ss, too
 * Jan  9 2001	Work on FitMatch()
 * Jan 11 2001	All diagnostic printing goes to stderr
 * Feb 28 2001	Ignore coordinate system if present after match file coordinates
 */ 
