import java.io.*;
import java.text.*;
import java.util.Locale;

abstract class f_xj
{
  // single function  multi independent variable
  // a single value is returned indiced to equation_ref
  // example f[0]=x[0]+sin(x[1])
  //         f[1]=x[0]*x[0]-x[1]
  // func(x,1) returns the value of f[1]
  // func(x,0) returns the value of f[0]

  abstract double func(double x[]);
}

public class HarmonySearch1 {
	public int m;        // number of variables
	public int HMS;      // Harmony memory size ( population size)
	public int maxIter;  // maximum number of iterations
	public double PAR;   // pitch adjusting rate
	public double BW;    // Bandwith
	public double HMCR;  // Harmony Memory consideration rate 0< HMCR< 1
	public double xmin[]; // minimum limits of independent variable 
	public double xmax[]; //maximum limits of independent variable
	public double NCHV[]; 
	public double bestFitHistory[];  // best fit history
	public double bestHarmony[];     // Best Harmony
	public double worstFitHistory[]; // worst fit history
	public double HM[][];             //Harmony memory
	static int generation = 0;
	public boolean terminationCriteria = true;
	f_xj ff;
	
    public HarmonySearch1(f_xj ffi,double xxmin[],double xxmax[],int maxIteration,double BWi,double HMCRi,int HMSi,double PARi)
    {	ff=ffi;
	    maxIter=maxIteration;    
	    m=xxmin.length;  
	    HMCR=HMCRi;
	    HMS=HMSi;
	    BW=BWi;  
	    xmin = xxmin;
		xmax = xxmax;
		NCHV = new double[m];
		bestHarmony = new double[m + 1];
		bestFitHistory = new double[maxIter + 1];
		worstFitHistory = new double[maxIter + 1];
		HM = new double[HMS][m + 1];
	    xmin=xxmin;
	    xmax=xxmax;
    }
	
    public double random(double low,double high)
	{return low+Math.random()*(high-low);}
	
	public int random(int low, int high)
	{return (int)(Math.floor(Math.random()*(high-low)+low+.5));}
	
	public void initiator() 
	{
		int i;
		double curFit;
		for (i = 0; i < HMS; i++) 
		{
			for (int j = 0; j < m; j++) {HM[i][j] = random(xmin[j], xmax[j]);}
			curFit = ff.func(HM[i]);  // fitness(HM[i]);
			HM[i][m] = curFit;        // the fitness is stored in the last column of HM
		}
	}

	public boolean stopCondition() {
		if (generation > maxIter)
			terminationCriteria = false;
		return terminationCriteria;
	}

	public void updateHarmonyMemory(double newFitness) {

		// find worst harmony
		double worst = HM[0][m];
		int worstIndex = 0;
		for (int i = 0; i < HMS; i++)
			if (HM[i][m] > worst) {
				worst = HM[i][m];
				worstIndex = i;
			}
		worstFitHistory[generation] = worst;
		// update harmony
		if (newFitness < worst) {
			for (int k = 0; k < m; k++)
				HM[worstIndex][k] = NCHV[k];
			HM[worstIndex][m] = newFitness;
		}
		// find best harmony
		double best = HM[0][m];
		int bestIndex = 0;
		for (int i = 0; i < HMS; i++)
			if (HM[i][m] < best) {
				best = HM[i][m];
				bestIndex = i;
			}
		bestFitHistory[generation] = best;
		if (generation > 0 && best != bestFitHistory[generation - 1]) {
			for (int k = 0; k < m; k++)
				bestHarmony[k] = HM[bestIndex][k];
			bestHarmony[m] = best;
		}
	}

	public void memoryConsideration(int varIndex) { NCHV[varIndex] = HM[random(0, HMS - 1)][varIndex];}

	public void pitchAdjustment(int varIndex) {

		double rand = Math.random();
		double temp = NCHV[varIndex];
		if (rand < 0.5) {
			temp += rand * BW;
			if (temp < xmax[varIndex])
				NCHV[varIndex] = temp;
		} else {
			temp -= rand * BW;
			if (temp > xmin[varIndex])
				NCHV[varIndex] = temp;
		}

	}

	public void randomSelection(int varIndex) {NCHV[varIndex] = random(xmin[varIndex], xmax[varIndex]);}

	public double[]  xmin() 
	{
		initiator();
		while (stopCondition()) {
			for (int i = 0; i < m; i++) {
				if (Math.random() < HMCR) {
					memoryConsideration(i);
					if (Math.random() < PAR)
						pitchAdjustment(i);
				} else
					randomSelection(i);
			}
			double currentFit;
			currentFit = ff.func(NCHV);//fitness(NCHV);
			updateHarmonyMemory(currentFit);
			generation++;
		}
		return bestHarmony;
	}
	
public String toString()
{ double x[]=xmin();
  double y=x[x.length-1];	
	String s="ymin = "+y+"\nxmin = ";
  for(int k=0;k<x.length-1;k++)
  {s+=x[k]+" ";}
  return s;
}

public void plot()
{
if(m==2)
{   double z[][]=Matrix.T(HM);
    for(int i=0;i<z[0].length;i++)
    {System.out.println(z[0][i]+"   "+z[1][i]);}
	Plot pp=new Plot(z[0],z[1]);
	pp.setPlotType(0,35);
	pp.setMinMax(xmin[0],xmax[0],xmin[1],xmax[1]);
	pp.plot();
}
}

}
