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[]);
}

//Program to find minimum of the function
//for maximum finding change the sign of the function

public class bat1
{
int m,n;
// n number of bat
// m number of variables (space dimension) for each bat
double x[][]; //bat population
double y[]; // function value;
double f[];  //pulse frequency
double v[][]; //velocity
double ri[];  //rate of pulse emmission
double ymin;
double xmin[];
double A[];
double beta;
double fmin,fmax;//minimum and maximum velocities
double A0,Amin,r0;
double alpha,gamma;
double t;
int ngeneration;
int imin;
double xxmin[];
double xxmax[];
RandomGenerator randGen = new RandomGenerator(1234);

public bat1(f_xj ff,int ni, double xxmini[],double xxmaxi[],int ngenerationi)
{
// initial population set
xxmin=xxmini;
xxmax=xxmaxi;
fmin=0;
fmax=0.656757575;
A0=0.5;
Amin=0;
r0=0.4864756;
alpha=0.878967896;
gamma=0.918679;
n=ni;
m=xxmin.length;
x=new double[n][m];
v=new double[n][m];
y=new double[n];
ri=new double[n];
f=new double[n];
xmin=new double[m];
A=new double[n];
double eps;
double AA=0;
ngeneration=ngenerationi;
// initilize bat population
for(int i=0;i<n;i++)
{ ri[i]=Math.random();
  for(int j=0;j<m;j++)
  {x[i][j]=randGen.randVal(xxmin[j],xxmax[j]);}
  y[i]=ff.func(x[i]);
  f[i]=fmin+(fmax-fmin)*randGen.ran1();
  checkmin(i);
}
//initilize bat velocities
for(int i=0;i<n;i++)
{for(int j=0;j<m;j++)
  {
  v[i][j]= v[i][j]+(x[i][j]-xmin[j])*f[i];
  }
}
//initilize Ai and ri
for(int i=0;i<n;i++)
{ri[i]=r0*randGen.ran1();
 A[i]=A0;
}
int l;
t=0;l=0;
while(l<ngeneration)
{ 
for(int i=0;i<n;i++)
{for(int j=0;j<m;j++)
  {x[i][j]= x[i][j]+v[i][j];
   //if out of range select a random location
   if(x[i][j]<xxmin[j])  x[i][j]=randGen.randVal(xxmin[j],xxmax[j]);
   if(x[i][j]>xxmax[j])  x[i][j]=randGen.randVal(xxmin[j],xxmax[j]);
  }
  y[i]=ff.func(x[i]);
  f[i]=randGen.randVal(fmin,fmax);
  checkmin(i);
}
for(int i=0;i<n;i++)
{for(int j=0;j<m;j++)
  {v[i][j]= v[i][j]+(x[i][j]-xmin[j])*f[i];}
}
for(int i=0;i<n;i++)
{ri[i]=r0*randGen.ran1()*(1-Math.exp(-gamma*t));
 A[i]=alpha*A[i];
}
//random walk
for(int i=0;i<n;i++)
{if(Math.random()>ri[i])
 {x[i]=local_solution(xmin,xxmin,xxmax,0.05); 
  y[i]=ff.func(x[i]);  
   checkmin(i);
  }
}

for(int i=0;i<n;i++)
{ if(Math.random()<A[i] && y[i]<ymin)
  AA=avg(A);	
 for(int j=0;j<m;j++)
 {eps=randGen.randVal(-1.0,1.0);
 x[i][j]=x[i][j]+eps*AA;}
 y[i]=ff.func(x[i]);  
 checkmin(i);
  ri[i]=r0*randGen.ran1()*(1-Math.exp(-gamma*t));
  A[i]=alpha*A[i]; 
}

for(int i=0;i<n;i++)
{  checkmin(i);}
    l++;
    t=l;
}//end of while

}

// if minimum is found replace ymin and xmin
// with the new founded value
public void checkmin(int i)
{
 if(y[i]<ymin) 
    {ymin=y[i];imin=i;
    for(int j=0;j<m;j++)
    {xmin[j]=x[i][j];}   
    }	
}

public static double avg(double xx[])
{ double toplam=0;
  int nn=xx.length;
  for(int i=0;i<nn;i++)
  {toplam+=xx[i];}
  toplam/=nn;
  return toplam;
}

//Gauss random number distributed around xavg within a diameter of r
public  double  gaussrandom(double xavg,double r)
{ int iset=0;
 double gset=0;
 double fac,rsq,v1,v2;
 double aret=0;
 if(iset==0)
 {
   do{
	 v1=randGen.randVal(-1.0,1.0);	 
	 v2=randGen.randVal(-1.0,1.0);
	 rsq=v1*v1+v2*v2;  
     }while(rsq>=1.0 || rsq==0.0);	   
   fac=Math.sqrt(-2.0*Math.log(rsq)/rsq);
   gset=v1*fac;
   iset=1;
   aret=v2*fac;
 }
 else
 {
 iset=0;
 aret=gset;
 }
 return aret/8.0*r+xavg;	
}

//Finds local solutions in the neighbourhood of the selected best soluton
// by using Gauss random number
public  double[] local_solution(double selected_best[],double xxmin[],double xxmax[],double ratio)
{ 
int m=xxmin.length;
double dx=1;
  double xx[]=new double[m];
  for(int j=0;j<m;j++)
  {
  dx=(xxmax[j]-xxmin[j])*ratio;	  
  xx[j]=gaussrandom(selected_best[j],dx);
  if(xx[j]<xxmin[j]) xx[j]=xxmin[j]+0.001*randGen.ran1()*dx;
   if(xx[j]>xxmax[j]) xx[j]=xxmax[j]-0.001*randGen.ran1()*dx; 
  }
  return xx;	
}
//output function
public double[] xmin()
{ return xmin;}

//output function
public String toString()
{ String s="ymin = "+ymin+"\nxmin = ";
  for(int k=0;k<xmin.length;k++)
  {s+=xmin[k]+" ";}
  return s;
}

public void plot()
{
// m number of variables (space dimension) for each bat
if(m==2)
{   double z[][]=Matrix.T(x);
    //for(int i=0;i<n;i++)
    //{System.out.println(z[0][i]+"   "+z[1][i]);}
	Plot pp=new Plot(z[0],z[1]);
	pp.setPlotType(0,35);
	pp.setMinMax(xxmin[0],xxmax[0],xxmin[1],xxmax[1]);
	pp.plot();
}
}

}