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

public class Hunting_search
{
int N;
int HGS;//Number of solution vectores in group
double MML;//Maximum movement toward the leader
double HGCR;//Hunting group consideration rate
double HG[][];
double Lower[];
double Upper[];
double dummyHG[][];
double fitness[];
double best[];
double worst[];
double alfa;
double beta;
double Ramax;
double Ramin;
int maxiter;
int epochiter;
double min;
double max;
int indexmin;
int indexmax;
int d;
double EN;
double[] ENcount;
f_xj ff;


      public Hunting_search(f_xj iff,int iHGS,double iMML,double iHGCR,double iLower[],double[] iUpper,double ialfa,double ibeta,double iRamax,double iRamin,int imaxiter,int iepochiter)
      {
	   ff=iff;
	   HGS=iHGS;   
	   HGCR=iHGCR;
	   MML=iMML;
	   Ramax=iRamax;
	   Ramin=iRamin;
	   Lower=iLower;   
	   Upper=iUpper;
	   epochiter=iepochiter;
	   maxiter=imaxiter;
	   N=Upper.length;    
	   HG=new double[HGS][N];
	   dummyHG=new double[HGS][N];   
	   fitness=new double[HGS];
	   best=new double[N];
	   worst=new double[N];
	   alfa=ialfa;
	   beta=ibeta;
	   EN=(double)maxiter/((double)HGS*(double)epochiter);
	   EN=Math.ceil(EN);
	 
	    //System.out.println(EN);
	    ENcount=new double[(int)EN];
	    for(int i=0;i<(int)EN;i++)
	    {ENcount[i]=i+1;}
	   // System.out.println(Matrix.toString(ENcount));
	  }


	 

	  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;	 
	  }
	  
	  void init()
	  {
	      for(int i=0;i<HGS;i++)
	      {
		     for(int j=0;j<N;j++)
		     {HG[i][j]=Lower[j]+(Math.random()*(Upper[j]-Lower[j]));}    
		  }	  
	 	  
		  //Calculate fitness
		  for(int i=0;i<HGS;i++)
		  {fitness[i]=ff.func(HG[i]);}
		  
		  // get minimum index and find best
		  double[] dd=getminval_index(fitness);
		  indexmin=(int)dd[1];
		  for(int i=0;i<N;i++)
		  {best[i]=HG[indexmin][i];}
		  
		  // get maximum index
		  double[] dd2=getmaxval_index(fitness);
		  indexmax=(int)dd2[1];
		  for(int i=0;i<N;i++)
		  {worst[i]=HG[indexmax][i];}
	  }
	  
	  double max(double x[])
	  {
	   int n=x.length;	  
	   double maxv=x[0];
	   for(int i=0;i<n;i++)
	   {if(x[i]>maxv) 
		  {maxv=x[i];}}	 
	   return maxv;
	  }
	  
	  double min(double x[])
	  {
	   int n=x.length;
	   double minv=x[0];
	   for(int i=0;i<n;i++)
	   {if(x[i]<minv)
	     {minv=x[i];}}	  
	   return minv;	  
		  
	  }
	  
	  
	  double[][] iterations()
	  {
		  init();
		  //System.out.println(Matrix.toString(HG));
		  int iter=1;
		  while(iter<maxiter)
		  {
		  
		  for(int i=0;i<HGS;i++)
		  {for(int j=0;j<N;j++)
		     {dummyHG[i][j]=HG[i][j]+(2.0*(Math.random()-0.5)*MML*(best[j]-HG[i][j]));}}
		  
		  for(int i=0;i<HGS;i++)
		  {
		     if(ff.func(dummyHG[i])<ff.func(HG[i]))	  
		     {
			     for(int j=0;j<N;j++)
			     {HG[i][j]=dummyHG[i][j];}    
			 } 
		 }
		  
		 //Calculate fitness
		  for(int i=0;i<HGS;i++)
		  {fitness[i]=ff.func(HG[i]);}
		  
		  // get minimum index and find best
		  double[] dd=getminval_index(fitness);
		  indexmin=(int)dd[1];
		  for(int i=0;i<N;i++)
		  {best[i]=HG[indexmin][i];}
		  
		 
		  for(int i=0;i<HGS;i++)
		  { 
		     if(Math.random()<HGCR)
		     {
		        d=(int)(((double)HGS)*Math.random());	  
		        for(int j=0;j<N;j++)
		        {HG[i][j]=HG[d][j];}	
		     }
		     else
		     {
			    for(int j=0;j<N;j++)
			    { 
			    HG[i][j]=HG[i][j]+(2.0*(Math.random()-0.5))*Ramin*(max(HG[i])-min(HG[i]))*Math.exp(Math.log(Ramin/Ramax)*(double)iter/(double)maxiter);         
		        } 
			 }
		  }
		  
		  
		  int tt=(int)((double)iter/((double)HGS*(double)epochiter));
		  for(int i=0;i<HGS;i++)
		  {
		      for(int j=0;j<N;j++)
		      {HG[i][j]=best[j]+(2.0*(Math.random()-0.5))*(max(HG[i])-min(HG[i]))*alfa*Math.exp(-beta*ENcount[tt]);                   }	  
		  }
	      
		  
		  
		  
		  //Calculate fitness
		  for(int i=0;i<HGS;i++)
		  {fitness[i]=ff.func(HG[i]);}
		  
		  // get minimum index and find best
		  double[] dd3=getminval_index(fitness);
		  min=dd3[0]; 
		  indexmin=(int)dd3[1];
		  for(int i=0;i<N;i++)
		  {best[i]=HG[indexmin][i];}
	      
		  iter++;
		  }
		  double[][] out=new double[2][N];
		  for(int i=0;i<N;i++)
		  {out[0][i]=best[i];}
		  out[1][0]=min;
		  return out;
		  
}
	  
	      void toStringnew()
	      {
		    double[][] in=iterations();  
		    System.out.println("Optimized value = "+in[1][0]);
		    for(int i=0;i<N;i++)
		    {System.out.println("x["+i+"] = "+in[0][i]);}  
		  }
        
}