import java.text.*;

public class AmoebaMinimizer implements Minimizer {
    protected double p[][];
    protected double y[];
    protected int ihi, ilo;
    protected double[] parm;

    public double ftol = 1.0e-10;
    public int nfunk = 0;

    public boolean verbose = false;

    public AmoebaMinimizer() {}

    public AmoebaMinimizer(double[] setparm) { init(setparm); }
       
    public void init(double[] setparm) {
	parm = setparm;
	p = new double[parm.length+1][parm.length];
	y = new double[parm.length+1];

	for(int i=0; i<parm.length+1; i++)
	    for(int j=0; j<parm.length; j++)
		p[i][j] = parm[j];

	for(int i=0; i<parm.length; i++) {
	    p[i+1][i]*=1.10;
	}

	for(int i=0; i<parm.length+1; i++) y[i] = evaluate(p[i]);
	if(verbose) System.out.println("Minimizer> Initial x^2=" + new DecimalFormat("0.00E0").format(y[0]));
    }

    public double minimize() {
	amoeba();
	for(int i=0; i<parm.length; i++) parm[i] = p[ilo-1][i];
	if(verbose) System.out.println("Minimizer> Final   x^2=" + new DecimalFormat("0.00E0").format(y[ilo-1]));
	return y[ilo-1];
    }

    int NMAX=10000;

    private void amoeba() {
	int ndim = y.length-1;
	int i,inhi,j,mpts=ndim+1;
	double rtol,sum,swap,ysave,ytry, psum[];
	double temp;

	psum = new double[ndim];
	
	for (j=1;j<=ndim;j++) {
	    for (sum=0.0,i=1;i<=mpts;i++) sum += p[i-1][j-1];
	    psum[j-1]=sum;
	}
	
	for (;;) {
	    ilo=1;
	    if(y[0]>y[1]) { ihi=1; inhi=2; } else { ihi=2; inhi=1; }
	    for (i=1;i<=mpts;i++) {
		if (y[i-1] <= y[ilo-1]) ilo=i;
		if (y[i-1] > y[ihi-1]) {
		    inhi=ihi;
		    ihi=i;
		} else if (y[i-1] > y[inhi-1] && i != ihi) inhi=i;
	    }
	    rtol=2.0*Math.abs(y[ihi-1]-y[ilo-1])/(Math.abs(y[ihi-1])+Math.abs(y[ilo-1]));
	    if (rtol < ftol) {
		temp = y[0]; y[0]=y[ilo-1]; y[ilo-1]=temp;
		for (i=1;i<=ndim;i++) 
		    { temp = p[0][i-1]; p[0][i-1]=p[ilo-1][i-1]; p[ilo-1][i-1]=temp; }
		break;
	    }
	    if (nfunk >= NMAX) { System.out.println("NMAX exceeded"); return; }
	    nfunk += 2;
	    ytry=amotry(psum,ndim,ihi,-1.0);
	    if (ytry <= y[ilo-1])
		ytry=amotry(psum,ndim,ihi,2.0);
	    else if (ytry >= y[inhi-1]) {
		ysave=y[ihi-1];
		ytry=amotry(psum,ndim,ihi,0.5);
		if (ytry >= ysave) {
		    for (i=1;i<=mpts;i++) {
			if (i != ilo) {
			    for (j=1;j<=ndim;j++)
				p[i-1][j-1]=psum[j-1]=0.5*(p[i-1][j-1]+p[ilo-1][j-1]);
			    y[i-1]=evaluate(psum);
			}
		    }
		    nfunk += ndim;
		    
		    for (j=1;j<=ndim;j++) {
			for (sum=0.0,i=1;i<=mpts;i++) sum += p[i-1][j-1];
			psum[j-1]=sum;
		    }
		    
		}
	    } else --nfunk;
	}
    }


    double amotry(double psum[], int ndim, int ihi, double fac)
    {
	int j;
	double fac1,fac2,ytry,ptry[];
	
	ptry = new double[ndim];
	fac1=(1.0-fac)/ndim;
	fac2=fac1-fac;
	for (j=1;j<=ndim;j++) ptry[j-1]=psum[j-1]*fac1-p[ihi-1][j-1]*fac2;
	ytry=evaluate(ptry);
	if (ytry < y[ihi-1]) {
	    y[ihi-1]=ytry;
	    for (j=1;j<=ndim;j++) {
		psum[j-1] += ptry[j-1]-p[ihi-1][j-1];
		p[ihi-1][j-1]=ptry[j-1];
	    }
	}
	return ytry;
    }
    
    public double evaluate(double[] parm) { return 0.0; }
}
 
    
