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


public class simulatedannealing
{
	// Maximizing a function
    //simulated annealing
    // a version of simulated annealing statistical approach 
    double T; //Temperature
    genotype Vc;
    genotype Vn[];
    genotype best;
    genotype oldbest;
    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;
    int BESTLIMIT;
    int bestcounter;
    
    //f_xj f;  // function to be avaluated by genetic algorithm
    
    public simulatedannealing(int iPOPSIZE,int iNVARS,int iN,double Ti)
    {
	T=Ti; 
	T0=T;   
    POPSIZE=iPOPSIZE;
    NVARS=iNVARS; // number of variables equation to solve
    MAX=20;
    N=iN;            // number of bits represent each variable for example 32  
    Vn=new genotype[POPSIZE+1];
    local=false;
    GLIMIT=10;
    BESTLIMIT=3;
    k=100.0;
    k0=k; 
    bestcounter=0;  
	}
		
	public void setVc(double low[],double up[],f_xj fi)
	{
	//select originalgene andomly	
	Vc=new genotype(NVARS,low,up,N,fi);
	}
	public void setBest(genotype gi,f_xj fi)
	{
	//select originalgene andomly	
	best=new genotype(gi,fi);
	}
	public void setoldBest(f_xj fi)
	{
	//select originalgene andomly	
	oldbest=new genotype(best,fi);
	}
	
    public void setVc(genotype gi,f_xj fi)
	{
	//select original gene from other gene
	Vc=new genotype(gi,fi);
	}		
	public void setVn(f_xj fi)
	{//select neighbouring genes similar to the original one
	for(int i=0;i<POPSIZE+1;i++)
	  { Vn[i]=new genotype(Vc,fi);}	
	   mutate(fi);  
	}
			
	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());	  
	  Vn[mem].setFitness(fi);
	  if(Vn[mem].getFitness() > Vn[POPSIZE].getFitness())
	     {
		 Vn[POPSIZE]=Vn[mem];    
	     for(j=0;j<NVARS;j++)
	        {
	        Vn[POPSIZE].G[j].copyGene(Vn[mem].G[j]);
		    }//end of for(j=0..
		 }//end of if
	  }//end of for(mem=0;..
	}
	
	
	public void setRFCF(genotype Pi[])
	{
	//calculates relative and cumulative fitness functions	
	int mem;
	int PN=Pi.length;	
	double sum=0.0;
	//total fitness of Vn
	for(mem=0;mem<PN;mem++)
	  {sum+=Pi[mem].Fitness;} 
	//System.out.println("sum="+sum);   
	//calculate relative fitness of each genotype
	for(mem=0;mem<PN;mem++)
	  {Pi[mem].setRFitness(sum);}
	//calculate cumulative fitness
	Pi[0].setCFitness(Pi[0].getRFitness());  
    for(mem=1;mem<PN;mem++)
	  {Pi[mem].setCFitness(Pi[mem-1].getCFitness()+Pi[mem].getRFitness());
	  }
	}
	
	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 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
      Vn[member].G[var].mutate();
	  //after mutation recalculate the function value
      Vn[member].setFitness(fi); 
      }   	     	        
   }
      
   public double[] getBest(genotype b)
   {
   double aa[]=new double[NVARS+1];  
   for(int i=0;i < NVARS;i++)
     {
     aa[i]=b.getGene(i);
	 }
	 aa[NVARS]=b.getFitness();
   return aa;	      
   }
   
   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
     {
	 setoldBest(fi); 
     do
     {   setVn(fi);
         evaluate(fi);     
         if(Vn[POPSIZE].getFitness() > Vc.getFitness()) 
         {  setVc(Vn[POPSIZE], fi);
	        if(Vn[POPSIZE].getFitness() > best.getFitness()) 
	            {setBest(Vn[POPSIZE],fi); }	        
	      	if(oldbest.getFitness() == best.getFitness()) bestcounter++;  
	        if(bestcounter>BESTLIMIT) break; 
	        good_swap++;
         }
         else if(Math.random()<Math.exp((Vn[0].getFitness()-Vc.getFitness())/T))
         {   setVc(Vn[POPSIZE],fi);}
     if(report)
        System.out.println(Matrix.toString(getBest(best)));
     } 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);
   }      
}