import java.util.Random;
import java.io.*;
abstract class f_xj
{
abstract double func(double x[]);
}

public class differential_search_evolution
{
int maxiter;
int N;
int D;
double Globalminimum;
double[] Globalminimizer;
double[] Lower;
double[] Upper;
double[] BEST;
double[] XBEST;
double Scale;
double[][] r;
double[][] Superorganism;
double[][] Stopoversite;
double[] Stopoverindex;
double[] yStopoversite;
double[] ySuperorganism;
double[][] donor;
double[][] XX;
double[][] Y;
double[] yy;
double[][] U;
double[][] V;
double[] LOGISTIC;
double[] LOGISTIC2;
double[] yxDE;
double[] y;
double[] TEMPMIN;
double[] BESTSUPER;
double[] WORSTSUPER;
double p1;
double p2;  
double fitnessbest;
double CR;
double FF;
double to1;
double to2;
double Fl;
double Fu;
int k1;
int k2;
int k3;
int k4;
int k5;
f_xj ff;


public differential_search_evolution(f_xj iff,int iN,double[] iLower,double[] iUpper,int imaxiter)
{
maxiter=imaxiter;
ff=iff;
N=iN;
Lower=iLower;
Upper=iUpper;
D=Lower.length;	
Superorganism=new double[N][D];	
Stopoversite=new double[N][D];	
donor=new double[N][D];	
XX=new double[N][D];
U=new double[N][D];
V=new double[N][D];
r=new double[N][D];	
y=new double[N];
yy=new double[N];
yxDE=new double[N];
Stopoverindex=new double[N];
LOGISTIC=new double[N*D];
LOGISTIC2=new double[maxiter+3];
BESTSUPER=new double[D];
WORSTSUPER=new double[D];
Fl=0.1;
Fu=0.9;
to1=0.1;
to2=0.1;
FF=0.9;
CR=0.9;
Globalminimizer=new double[D];
TEMPMIN=new double[D];
BEST=new double[maxiter];
XBEST=new double[D];
yStopoversite=new double[N];
ySuperorganism=new double[N];
Globalminimum=1e10;
	
}

void inititializeLOG()
{
// Logistic map
	LOGISTIC2[0]=Math.random();
	for(int i=0;i<(maxiter)+2;i++)		
	{
		if((LOGISTIC2[i]==0.25)||(LOGISTIC2[i]==0.5)||(LOGISTIC2[i]==0.75))
    	{LOGISTIC2[i]=Math.random();}
		LOGISTIC2[i+1]=4.0*LOGISTIC2[i]*(1.0-LOGISTIC2[i]);
	}	
	
}


void initializeV()
{
    for(int i=0;i<N;i++)
    {for(int j=0;j<D;j++)
	   {V[i][j]=Lower[j]*Math.random()*(Upper[j]-Lower[j]);}}
}



void initializeDS()
{
  for(int i=0;i<N;i++)
  {for(int j=0;j<D;j++)
    {Superorganism[i][j]=Lower[j]+((Upper[j]-Lower[j])*Math.random());
    Superorganism[i][j]=Upper[j]+Lower[j]-Superorganism[i][j];}}	
  
  for(int i=0;i<N;i++)
  {ySuperorganism[i]=ff.func(Superorganism[i]);}
}

void initializeDE()
{
// Logistic map
LOGISTIC[0]=Math.random();
for(int i=0;i<(N*D)-1;i++)	
{
	if((LOGISTIC[i]==0.25)||(LOGISTIC[i]==0.5)||(LOGISTIC[i]==0.75))
    {LOGISTIC[i]=Math.random();}
	LOGISTIC[i+1]=4.0*LOGISTIC[i]*(1.0-LOGISTIC[i]);
}	
   int kk=0;
   for(int i=0;i<N;i++)
   {for(int j=0;j<D;j++)
      {XX[i][j]=Lower[j]+(LOGISTIC[kk]*(Upper[j]-Lower[j]));}}
      
      for(int i=0;i<N;i++)
      {yy[i]=ff.func(XX[i]);}
      
      double[] g1=getminval_index(yy);
      int m4=(int)g1[1];
      
      for(int i=0;i<D;i++)
      {XBEST[i]=XX[m4][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;	 
}

// Random permutation of matrix
double[][] randperm(double a[][])
{
double y[][]=new double[a.length][a[0].length];
double[] sw=new double[a[0].length];
for(int i=0;i<a.length;i++)
{y[i]=a[i];}
int m=a.length;
int rp=0;
for(int i=0;i<m-1;i++)
{rp=(int)((m-i)*Math.random()+i);sw=y[i];
y[i]=y[rp];y[rp]=sw;}
return y;	 
}

int randi(int up)
{return (int)Math.floor(up*Math.random());}

int[] randi(int up,int count)
{   
   int[] aa=new int[count]; 	
   for(int i=0;i<count;i++)
   {aa[i]=(int)Math.floor(up*Math.random());}
 return aa; 		
}

		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[][] solution()
{
initializeDS();
initializeDE();
inititializeLOG();
initializeV();
int dd[];
int mindex;
int iter=0;
int jrand=5;
   while(iter<maxiter)
   {
	  
	   
	   
	   
	   
	   for(int i=0;i<N;i++)
	   {
		  if(Math.random()<to1)
		  {FF=Fl+Math.random()*Fu;} 
		  if(Math.random()<to2)
		  {CR=Math.random();}
		  
	   	   k1=(int)Math.floor((double)N*Math.random());
	       k2=(int)Math.floor((double)N*Math.random());
	       k3=(int)Math.floor((double)N*Math.random());
	       k4=(int)Math.floor((double)N*Math.random());
	       k5=(int)Math.floor((double)N*Math.random());
	   
	       while(i==k1)
	       {k1=(int)Math.floor((double)N*Math.random());}
	   
	       while((k1==k2)||(i==k2))
	       {k2=(int)Math.floor((double)N*Math.random());}
	   
	       while((k2==k3)||(k1==k3)||(i==k3))
	       {k3=(int)Math.floor((double)N*Math.random());}
	   
	       while((k3==k4)||(k2==k4)||(k1==k4)||(i==k4))
	       {k4=(int)Math.floor((double)N*Math.random());}
	   
	       while((k4==k5)||(k3==k5)||(k2==k5)||(k1==k5)||(i==k5))
	       {k5=(int)Math.floor((double)N*Math.random());}
		  
		  
		   
		   // DE/best/1 
	       if((double)i%5==0)
	       {  
		     jrand=(int)Math.floor(Math.random()*(double)D);  
		     for(int j=0;j<D;j++)
		     {
			    if((Math.random()<CR)||(j==jrand)) 
			    {V[i][j]=XBEST[j]+FF*(XX[k1][j]-XX[k2][j]);}
			    
			 }
		   } 	   
		  
		   // DE/best/2 
	       if((double)i%5==1)
	       { 
		     jrand=(int)Math.floor(Math.random()*(double)D);     
		     for(int j=0;j<D;j++)
		     {   
			     if((Math.random()<CR)||(j==jrand))
		         {V[i][j]=XBEST[j]+(FF*(XX[k1][j]-XX[k2][j]))+(FF*(XX[k3][j]-XX[k4][j]));}
		         
			 }    
		   } 	    
		   
		   // DE/rand/2 
	       if((double)i%5==2)
	       {  
		     jrand=(int)Math.floor(Math.random()*(double)D); 
		     for(int j=0;j<D;j++)
		     {
			     if((Math.random()<CR)||(j==jrand))
			     {V[i][j]=XX[k1][j]+(FF*(XX[k2][j]-XX[k3][j]))+(FF*(XX[k4][j]-XX[k5][j]));}
			     
			 }    
		   }  
		   
		   // DE/current to rand/1 
	       if((double)i%5==3)
	       {  
		     jrand=(int)Math.floor(Math.random()*(double)D);   
		     for(int j=0;j<D;j++)
		     {  
			     if((Math.random()<CR)||(j==jrand))
			     {V[i][j]=XX[i][j]+(FF*Math.random()*(XX[k1][j]-XX[k2][j]))+(FF*(XX[k2][j]-XX[k3][j]));}
			     
			 }    
		   }  
		   
		   
		   // Chaotic-exponential based mutation 
		   if((double)i%5==4)
	       {  
		     jrand=(int)Math.floor(Math.random()*(double)D);   
		     for(int j=0;j<D;j++)
		     {
			     if((Math.random()<CR)||(j==jrand))
			     {V[i][j]=XX[k1][j]+LOGISTIC2[iter]*Math.abs(Math.exp(-(double)iter/(double)maxiter))*(XX[k2][j]-XX[k3][j]);}
			 }    
		   }
		   
		   
	   }
	   
	   
	   // Greedy selection
	   for(int i=0;i<N;i++)
	   {
	   	   if(ff.func(V[i])<ff.func(XX[i]))
	   	   for(int j=0;j<D;j++)
	   	   {XX[i][j]=V[i][j];}
	   }
	   
	   // Boundary control of DE individuals
	   for(int i=0;i<N;i++)
	   {for(int j=0;j<D;j++)
	      {if((XX[i][j]<Lower[j])||(XX[i][j]>Upper[j]))
		    {XX[i][j]=Lower[j]+Math.random()*(Upper[j]-Lower[j]);}}}
	   
	   
	   for(int i=0;i<N;i++)
	   {yxDE[i]=ff.func(XX[i]);}
	   
	   
	  double[] b1=getminval_index(yxDE);
	  int mindexb1=(int)b1[1];
	  
	  // best individual in XX (DE individuals)
	  for(int j=0;j<D;j++)
	  {TEMPMIN[j]=XX[mindexb1][j];} 
	  
	  
	  // Deciding the best and the worst individuals in Superorganism
	   for(int i=0;i<N;i++)
	   {Stopoverindex[i]=ff.func(Superorganism[i]);}
	    double[] b2=getminval_index(Stopoverindex);
	   int mindexb2=(int)b2[1];
	   double[] w2=getmaxval_index(Stopoverindex);
	   int maxdexw2=(int)w2[1];
	   for(int j=0;j<D;j++)
	   {BESTSUPER[j]=Superorganism[mindexb1][j];}
	   for(int j=0;j<D;j++)
	   {WORSTSUPER[j]=Superorganism[maxdexw2][j];}
	   
	   // Updating Superorganism individuals with the best values obtained so far
	    if(ff.func(TEMPMIN)<ff.func(WORSTSUPER))
	    {
		   if(ff.func(TEMPMIN)<ff.func(BESTSUPER))
		   {for(int j=0;j<D;j++)
			 {Superorganism[mindexb1][j]=TEMPMIN[j];}}    
		   
		   for(int j=0;j<D;j++)
		   {Superorganism[maxdexw2][j]=TEMPMIN[j];}	  
		}
	   
	   
	// Differential search phase   
	   
	donor=randperm(Superorganism);	
	
	p1=0.3*Math.random();
	p2=0.3*Math.random();
	    if(Math.random()<Math.random())
	    {
		      if(Math.random()<p1)
		      { // Random-mutation I strategy
			    for(int i=0;i<N;i++)
			    {for(int j=0;j<D;j++)
				  {if(Math.random()<Math.random())
					{r[i][j]=1.0;}}}
		      }		
			  else
			  { // Diferential mutation strategy
			     for(int i=0;i<N;i++)
			     {r[i][randi(D)]=1.0;} 	   
			  }		
		}
	    else
	    {
		       // Random-mutation II strategy 
		       for(int i=0;i<N;i++)
		       {dd=randi(D,(int)(p2*D));
			      for(int j=0;j<dd.length;j++)
			      {r[i][dd[j]]=1.0;}}     
		}
	
		Random rndm=new Random();
	    Scale=4.0*(rndm.nextGaussian());// brownian walk
	    // bio-interaction
	    for(int i=0;i<N;i++)
	     {for(int j=0;j<D;j++)
	      {Stopoversite[i][j]=Superorganism[i][j]+((Scale*r[i][j])*(donor[i][j]-Superorganism[i][j]));}}
	   
	    // Boundary control;
	    for(int i=0;i<N;i++)
	    {for(int j=0;j<D;j++)
		  {if((Stopoversite[i][j]<Lower[j])||(Stopoversite[i][j]>Upper[j]))
		    {Stopoversite[i][j]=Lower[j]+(Math.random()*(Upper[j]-Lower[j]));}}}
		
		// Selection II
		for(int i=0;i<N;i++)
		{yStopoversite[i]=ff.func(Stopoversite[i]);}
		
		for(int i=0;i<N;i++)
		{
		  if(yStopoversite[i]<ySuperorganism[i])
		  {ySuperorganism[i]=yStopoversite[i];
		    for(int j=0;j<D;j++)
		    {Superorganism[i][j]=Stopoversite[i][j];}}
		}     
	 
		double x1[]=getminval_index(ySuperorganism);
		fitnessbest=x1[0];
		mindex=(int)x1[1];
		
		for(int j=0;j<D;j++)
	    {XBEST[j]=Superorganism[mindex][j];}
		
		if (fitnessbest<Globalminimum)
	    {
		     Globalminimum=fitnessbest;
	         for(int j=0;j<D;j++)
	         {Globalminimizer[j]=Superorganism[mindex][j];}
	    }
	    
	    
	    
	    
	     BEST[iter]=Globalminimum;
		 
	     
	     
	     iter++;
   }	
	        
   
   
   
   
   
            double[] plott=new double[maxiter];
		    for(int i=0;i<maxiter;i++)
		    {plott[i]=i;}
		    Plot pp=new Plot(plott,BEST);
		    //pp.plot();
      double out[][]=new double[2][Globalminimizer.length];
	  out[0][0]=Globalminimum;out[0][1]=Globalminimizer.length;
	  for(int i=0;i<Globalminimizer.length;i++)
	  {out[1][i]=Globalminimizer[i];}
	  return out;
	
}



void toStringnew()
{
double[][] out=solution();
System.out.println("Optimized value = "+out[0][0]);
for(int i=0;i<out[0][1];i++)
{System.out.println("x["+i+"] = "+out[1][i]);}
}






void toTxtfile()
		{   String named="diffevol_f20_levy50.txt";
			String line="";
			int lineNo;
			try
			   {
			   FileWriter fw=new FileWriter(named,true); 	   
			   BufferedWriter bw=new BufferedWriter(fw);
			   LineNumberReader lnr=new LineNumberReader(new FileReader(named));   
			   lnr.setLineNumber(1);
			   for(int i=1;i<=lnr.getLineNumber();i++)
			   {bw.newLine();}
			   double[][] dd=solution();
			   Double d1d=new Double(dd[0][0]);
			   String s1=d1d.toString();
			   int nn=dd[1].length;
			   Double[] val=new Double[nn];
			   String[] str=new String[nn];
			   for(int i=0;i<nn;i++)
			   {val[i]=new Double(dd[1][i]);}
			   for(int i=0;i<nn;i++)
			   {str[i]=val[i].toString();}
			   bw.write(s1+" ");
			   for(int i=0;i<nn;i++)
			   {bw.write(str[i]+" ");}
			   bw.close();
			   lnr.close();
		       }catch(IOException e)
		       {e.printStackTrace();}
			   
		}		 



}