////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Referance : An Intelligent Tuned Harmony Search algorithm for optimisation
//Parikshit Yadav, Rajesh Kumar, S.K. Panda, C.S. Chang
// Programmed by : Ošuz Emrah Turgut 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

abstract class f_xj
{
abstract double func(double x[]);
}

public class intelligent_tuned_harmony_search
{
int N;
double Lower[];
double Upper[];
int HMS;
double HMCR;
double PARmin;
double PARmax;
double PAR;
int Maxiter;
double best[];
double worst[];
double y[];
int d;
int m;
double[][] HM;
double[] fitness;
double[] xdot;
int indexmin;
double minval;
int indexmax;
double maxval;
double fitnessmean;
f_xj ff;
             public intelligent_tuned_harmony_search(f_xj iff,double[] iLower,double[] iUpper,int iHMS,double iHMCR,int iMaxiter)
             {
	         ff=iff;    
	         Maxiter=iMaxiter;   
	         Lower=iLower;
	         Upper=iUpper;
	         N=Upper.length;        
	         HMS=iHMS;    
	         HMCR=iHMCR;    
	         PARmin=0.0;
	         PARmax=2.0;    
	         HM=new double[HMS][N];
	         fitness=new double[HMS];
	         best=new double[N];
	         worst=new double[N];    
	         y=new double[N];
	         xdot=new double[N];
	         }
          
	         void initialize()
	         {
		      // introduce Harmony(improvisation)   
	          for(int i=0;i<HMS;i++)
	            {for(int j=0;j<N;j++)
		           {HM[i][j]=Lower[j]+(Upper[j]-Lower[j])*Math.random();}}
		           
                    // calculate fitness 		          
		            for(int i=0;i<HMS;i++)
		          {fitness[i]=ff.func(HM[i]);}
		          
		          // get minimum index
		          double[] dd=getminval_index(fitness);
		          minval=dd[0];
		          indexmin=(int)dd[1];
		          for(int i=0;i<N;i++)
		          {best[i]=HM[indexmin][i];}
		          
		          // get maximum index
		           double[] dd2=getmaxval_index(fitness);
		           maxval=dd2[0];
		           indexmax=(int)dd2[1];
		           for(int i=0;i<N;i++)
		           {worst[i]=HM[indexmax][i];}
		    }

	         
	         double[] getminval_index(double[] a)
	         {
	         double m=0.0;	 
	         double b[]=new double[a.length];
	         for(int i=0;i<a.length;i++)
	         {b[i]=a[i];}  
	         double minval=a[0];
	         for(int i=0;i<a.length;i++)
	         {if(a[i]<minval){minval=a[i];}}
	         for(int i=0;i<a.length;i++)
	         {if(b[i]==minval){m=i;break;}};
	         double[] dep=new double[2];
	         dep[0]=minval;
	         dep[1]=m;
	         return dep;	 
	         }

	         double[] getmaxval_index(double a[])
	         {
	         double m=0.0;
	         double b[]=new double[a.length];
	         for(int i=0;i<a.length;i++)
	         {b[i]=a[i];}
	         double maxval=a[0];
	         for(int j=0;j<a.length;j++)
	         {if(a[j]>maxval){maxval=a[j];}}
	         for(int i=0;i<b.length;i++)
	         {if(b[i]==maxval){m=i;break;}}
	         double dep2[]=new double[2];
	         dep2[0]=maxval;
	         dep2[1]=m;
	         return dep2;	 
	         }         
		     double mean(double x[]) // mean of a vector
		     {
			 int n=x.length;double s1=0.0;
			 for(int i=0;i<n;i++)
			 {s1+=x[i];}    
			 return s1/(double)n;    
			 }	     
		     double[][] solution()
		     {
			   initialize() ; 
			   int iter=0;
			   while(iter<Maxiter)
			   { 
				    for(int i=0;i<N;i++)
				    {
					       if(Math.random()<HMCR)   
					       {
						    d=(int)(((double)HMS)*Math.random());
				            xdot[i]=HM[d][i];  		       
						    PAR=PARmax-((PARmax-PARmin)*((double)iter)/((double)Maxiter));   
						          if(Math.random()<PAR)
						          {
							         fitnessmean=mean(fitness);
							         if(fitness[d]<fitnessmean)
							         {
								        // Group A, Both intensification and Diversification   
								        if(Math.random()<=0.5)
								        {
									    y[i]=best[i]-(best[i]-HM[d][i])* Math.random();    
									    }  
								        else
								        {
									    y[i]=best[i]-(worst[i]-HM[d][i])*Math.random();    
								        }     
									 }  
								     else
							         { // Group B only diversification
								       m=(int)(((double)N)*Math.random());  
								       y[i]=xdot[i]+(best[m]-xdot[i])*Math.random();
								         
								     } 
								         
								     if((Lower[i]<=y[i])&&(Upper[i]>y[i]))
								     {xdot[i]=y[i];}
								         
								   }
						    }	    
							else       
							{
							xdot[i]=Lower[i]+(Upper[i]-Lower[i])*Math.random();  	   
						    }       
				  }
				  if(ff.func(xdot)<=ff.func(worst))	       
				  {
					 for(int i=0;i<N;i++)
					 {worst[i]=xdot[i];}
				  }		              
				  for(int i=0;i<HMS;i++)
		          {fitness[i]=ff.func(HM[i]);}
		          
		          // get minimum index and best solution
		          double[] dd=getminval_index(fitness);
		          minval=dd[0];
		          indexmin=(int)dd[1];
		          for(int i=0;i<N;i++)
		          {best[i]=HM[indexmin][i];}
		          
		          // get maximum index and replace new worst value
		           double[] dd2=getmaxval_index(fitness);
		           maxval=dd2[0];
		           indexmax=(int)dd2[1];
		           for(int i=0;i<N;i++)
		           {HM[indexmax][i]=worst[i];}	 
		           
		           iter++;  
		     }
		     double[][] dep=new double[2][N];
		     dep[0][0]=minval;
		     for(int i=0;i<N;i++)
		     {dep[1][i]=best[i];}
             return dep;		     	     
}	 
 
			void toStringnew()
			{
			 double[][] out=solution();
			 System.out.println("Optimized value = "+out[0][0]);
			 for(int i=0;i<N;i++)
			 {System.out.println("x["+i+"] = "+out[1][i]);}
			}		  
			}