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


public class SA
{


	// Maximizing a function
    //simulated annealing
    // a version of simulated annealing statistical approach 
    double T; //Temperature
    double Vc[];
    double Vn[][];
    double best[];
    int MAX;//number 
    int GLIMIT;//arka arkaya başarılı steplerin sayısı
    boolean local;
    int NVARS;//numberof variables 
    int N;//number of bit representation for each variable
    double T0;
    double k,k0;//time index
    int POPSIZE;
    double low[];
    double high[];
    
    //f_xj f;  // function to be avaluated by genetic algorithm
    
    public SA(int iPOPSIZE,int iNVARS,int iN,double Ti)
    {
	T=Ti; 
	T0=T;   
    POPSIZE=iPOPSIZE;
    NVARS=iNVARS; // number of variables equation to solve
    MAX=200;
    N=iN;            // number of bits represent each variable for example 32  
    local=false;
    GLIMIT=30;
    k=100.0;
    k0=k;
    Vc=new double[NVARS];
    Vn=new double[POPSIZE+1][NVARS];
    best=new double[NVARS];   
    low=new double[NVARS];
    high=new double[NVARS];
	}
	double gaussrandom()
    {
    //gaussian random number deviates	
    int iset=0;
	double gset=0;
	double fac,r,v1,v2;

	if  (iset == 0) {
		do {
			v1=2.0*Math.random()-1.0;
			v2=2.0*Math.random()-1.0;
			r=v1*v1+v2*v2;
		} while (r >= 1.0);
		fac=Math.sqrt(-2.0*Math.log(r)/r);
		gset=v1*fac;
		iset=1;
		return v2*fac;
	} else {
		iset=0;
		return gset;
	}
    }	
	public void setVc(double ilow[],double ihigh[],f_xj fi)
	{
		
	//select original gene randomly
	for(int i=0;i<NVARS;i++)
	   {low[i]=ilow[i];high[i]=ihigh[i];Vc[i]=(high[i]-low[i])*Math.random();}
    }
       	
	public void setBest(double gi[],f_xj fi)
	{
	//select originalgene andomly	
	for(int i=0;i<NVARS;i++)
	   {best[i]=gi[i];}
	}
	
    public void setVc(double gi[],f_xj fi)
	{
	//select original gene from other gene
		for(int i=0;i<NVARS;i++)
	   {Vc[i]=gi[i];}
	}
			
	public void setVn(f_xj fi)
	{//select neighbouring genes similar to the original one
	for(int i=0;i<POPSIZE+1;i++)
	  { for(int j=0;j<NVARS;j++)
	   {Vn[i][j]=Vc[i];}}	
	   mutate(fi);  
	}
   
   public void mutate(f_xj fi)
   {
   int i;
   double lbound,hbound;
   int nmutations;
   int member;
   int var;
   nmutations=(int)((double)(POPSIZE*NVARS*N));
   for(i=0;i<nmutations;i++)
      {
      member=(int)(Math.random()*POPSIZE);
      var=(int)(Math.random()*NVARS);
      //replace the old value with a new mutated one
      //neigboring factor
      double R=(high[var]-low[var])*Math.random()*0.15;
      Vn[member][var] += R;
      //System.out.println("gauss random = "+gg);
      if(Vn[member][var]>high[var]) Vn[member][var]=high[var]-Math.random()*0.12561462;
      else if (Vn[member][var]<low[var]) Vn[member][var]=low[var]+Math.random()*0.12561462;;
	  //after mutation recalculate the function value 
      }   	     	        
   }
	
			
	public void evaluate(f_xj fi)
	{
	  int mem;
	  int j;
	  double x[]=new double[NVARS];
	  for(mem=0;mem<POPSIZE;mem++)
	  {
	  //System.out.println(Vn[mem].toString());	  
	  if(getFitness(Vn[mem],fi) > getFitness(Vn[POPSIZE],fi))
	     {
		 //Vn[POPSIZE]=Vn[mem];    
	     for(j=0;j<NVARS;j++)
	        {
	        Vn[POPSIZE][j]=Vn[mem][j];
		    }//end of for(j=0..
		 }//end of if
	  }//end of for(mem=0;..
	}
	
		
	public void toString(genotype Pi[])
	{
	int mem;
	int PN=Pi.length;	
	for(mem=0;mem<PN;mem++)
	{
	//list them	
	System.out.println("Vn["+mem+"]="+Pi[mem].toString());
	} 	
	}

   public double[] getBest(double[] b,f_xj fi)
   {
   double aa[]=new double[NVARS+1];  
   for(int i=0;i < NVARS;i++)
     {
     aa[i]=b[i];
	 }
	 aa[NVARS]=getFitness(b,fi);
   return aa;	      
   }
   
   public double getFitness(double []xi,f_xj fi)
   {
   return fi.func(xi);
   }
   
   public double[] calculate(double low[],double up[],f_xj fi,boolean report)
   {
     int good_swap=0;
	 setVc(low,up,fi);// new set of random selection in each time	
     setBest(Vc,fi);
	 do
     {
     do
     {   setVn(fi);
         evaluate(fi);     
         if(getFitness(Vn[POPSIZE],fi) > getFitness(Vc,fi)) 
         {  setVc(Vn[POPSIZE], fi);
	        if(getFitness(Vn[POPSIZE],fi) > getFitness(best,fi)) setBest(Vn[POPSIZE],fi);
	        good_swap++;
         }
         else if(Math.random()<Math.exp((getFitness(Vn[0],fi)-getFitness(Vc,fi))/T))
         {   setVc(Vn[POPSIZE],fi);}
     if(report)
        System.out.println(Matrix.toString(getBest(best,fi)));
     } while(good_swap<GLIMIT);
     k+=1.0;
     double lk=Math.log(k);
     T=T-Math.log(k0)/(k*lk*lk);
     setVc(low,up,fi); //new random value      
     } while(k<MAX);	 
   return getBest(best,fi);
   }      
}