
//filename:   GAClass.cpp

#include <fstream.h> 
#include <iostream.h> 
#include <stdlib.h> 
#include <time.h> 
#include <math.h>

//for black box 's fitness evaluation
//double eval(int *);

// for De Jong function 1 's fitness evaluation
double eval1(int *);
void decode1(int *, double &, double &, double &);

// for De Jong function 2 's fitness evaluation
double eval2(int *);
void decode2(int *, double &, double &);

// for De Jong function 3 's fitness evaluation
double eval3(int *);
void decode3( int *,double *);

// for De Jong function 4 's fitness evaluation
double eval4(int *);
void decode4(int *, double *);


//--------------------------------------------------------------
//     Class: for single chromosome:
//        declaration and definition
//--------------------------------------------------------------
class Chromosome {

public:

  int *chrom;
  double fitness;

  Chromosome(int chromSize);
  Chromosome() { };
  void initialize(int);

private:

  int size;

};
/*
Chromosome::Chromosome(int chromSize)
{

  size = chromSize;
  chrom = new int [chromSize];

}
*/
void Chromosome::initialize(int chromSize)
{
  
  size = chromSize;
  chrom = new int [chromSize];

  for (int i = 0; i < size; i++)
    chrom[i] = rand() % 2;

  return;

}



//--------------------------------------------------------------
//     Class: for whole population
//        declaration and definition
//--------------------------------------------------------------

class Population {

public:

  Population(int, int, int);
  void initialize(double, double);
  void evolution();
  

  //private:

  Chromosome *oldChroms;
  Chromosome *newChroms;
  Chromosome *temp;            //for temporary use

  Chromosome maxChrom;
  double maxFitness;
  double sumFitness;
  double avgFitness;

  double currentMaxFit;
  double currentMinFit;
  int currentMaxChromIndex;

  int chromLength;
  int popSize;
  int totalNumGenerations;
  int currentGenNum;
  
  double crossProbability;
  double mutateProbability;

  void swapPop();
  int flip(double);
  void mutate(Chromosome *);
  void evaluate();
  void statistics();
  int roulette();
  void crossover(int, int, int, int);
  void generateNewPop();
  void reportResult();


};

Population::Population(int chromL, int popS, int totalNumGens)
{

  int i;

  oldChroms = new Chromosome [popS];
  newChroms = new Chromosome [popS];
  totalNumGenerations = totalNumGens;

  popSize = popS;
  chromLength = chromL;
  
}

void Population::initialize(double crossP, double mutateP)
{

  int i;
  
  //srand(time(0));
  //srand48(time(0));

  crossProbability = crossP;
  mutateProbability = mutateP;

  for (i = 0; i < popSize; i++) {
    oldChroms[i].initialize(chromLength);
    newChroms[i].initialize(chromLength);  
  }

  maxChrom.initialize(chromLength);
  maxFitness = 0.0;

  currentMaxFit = 0.0;
  currentMinFit = 99999.9;
  currentMaxChromIndex = -1;

  currentGenNum = 0;

  sumFitness = 0.0;
  avgFitness = 0.0;

  
  return;
}

void Population::swapPop()
{

  temp = oldChroms;
  oldChroms = newChroms;
  newChroms = temp;

  return;

}


void Population::evaluate()
{
  for (int i = 0; i < popSize; i++) {
    //oldChroms[i].fitness = eval1(oldChroms[i].chrom);
    //oldChroms[i].fitness = eval2(oldChroms[i].chrom);
    oldChroms[i].fitness = eval3(oldChroms[i].chrom);
    //oldChroms[i].fitness = eval4(oldChroms[i].chrom);

  }

  return;

}


//calculate sumFitness avgFitness, and 
//   the maxFitness and its corresponding
//   chromosome
void Population::statistics()
{
  int i;

  currentMaxFit = 0.0;
  currentMinFit = 99999.9;
  currentMaxChromIndex = -1;
  sumFitness = 0.0;
  avgFitness = 0.0;

  for (i = 0; i < popSize; i++) {
    sumFitness += oldChroms[i].fitness;
    if (oldChroms[i].fitness > currentMaxFit) {
      currentMaxFit = oldChroms[i].fitness;
      currentMaxChromIndex = i;
    }
   if (oldChroms[i].fitness < currentMinFit) {
      currentMinFit = oldChroms[i].fitness;
    } 
  }

  avgFitness = sumFitness / popSize;

}

void Population::reportResult()
{
    maxChrom.fitness = maxFitness;
    cout<<currentGenNum;
    cout << "  "<<currentMinFit<<"  "<<currentMaxFit;
    cout<<"  "<<avgFitness<<endl;
    /*
    for (int i = 0; i < chromLength; i++) {
      maxChrom.chrom[i] = oldChroms[currentMaxChromIndex].chrom[i];
      cout<<maxChrom.chrom[i];
    }
    cout<<endl;
    */
}
int Population::flip(double prob)
{

  if (prob == 1.0)
    return 1;
  else
    return (drand48() <= prob);

}

void Population::mutate(Chromosome *chr)
{
  if (flip(mutateProbability)) {
    int k;
    k = rand() % chromLength;
    chr->chrom[k] = (chr->chrom[k] + 1) % 2;
  }
}


int Population::roulette()
{
  int k;
  double hitSum, randNum, accumulateSum;

  hitSum = drand48() * sumFitness;
  accumulateSum = 0.0; 
  k = 0;

  while ((k < popSize) && (accumulateSum <= hitSum)) {
    accumulateSum += oldChroms[k].fitness;
    k++;
  }

  k--;
  
  return k;

}

void Population::crossover(int p1, int p2, int c1, int c2)
{
  int i, k;

  if (flip(crossProbability)) {
    k = rand() % chromLength;
    for (i = 0; i < k; i++) {
      newChroms[c1].chrom[i] = oldChroms[p1].chrom[i];
      newChroms[c2].chrom[i] = oldChroms[p2].chrom[i];
    }
    for (i = k; i < chromLength; i++) {
      newChroms[c1].chrom[i] = oldChroms[p2].chrom[i];
      newChroms[c2].chrom[i] = oldChroms[p1].chrom[i];
    }
  }
  else 
    for (i = 0; i < chromLength; i++) {
      newChroms[c1].chrom[i] = oldChroms[p1].chrom[i];
      newChroms[c2].chrom[i] = oldChroms[p2].chrom[i];
    }

  mutate(&newChroms[c1]);
  mutate(&newChroms[c2]);

}

void Population::generateNewPop()
{
  int i, p1, p2;
  
  for (i = 0; i< popSize; i += 2) {
    p1 = roulette();
    p2 = roulette();
    crossover(p1, p2, i, i+1);
  }
}


void Population::evolution()
{
  for (int i = 0; i < totalNumGenerations; i++) {
    currentGenNum = i;
    evaluate();
    statistics();
    reportResult();
    generateNewPop();
    swapPop();
  }

}


// for De Jong function 1 's fitness evaluation
//   with the use CMAX = 100.0 for adjusment 
//       of the fitness
double eval1(int *vec)
{
  double x,y,z;
  
  x = y = z = 0.0;
  decode1(vec,x,y,z);
  
  return (100.0 - ((x*x)+(y*y)+(z*z)));

}
void decode1( int *vec,double &x,double &y,double &z  )
{
  int i;
  double k;

  x = y = z = 0.0;
   
  for (i = 1; i < 10; i++) {
    k = pow(2, i - 1);
    x += vec[i] * k;
    y += vec[10 + i] * k;
    z += vec[20 + i] * k;

  }
  k = pow(2, 9);
  x -= vec[0] * k; x /= 100.0;
  y -= vec[10] * k; y /= 100.0;
  z -= vec[20] * k; z /= 100.0;

  return;

}


// for De Jong function 2 's fitness evaluation
//   with the use CMAX = 4000.0 for adjusment 
//       of the fitness
double eval2(int *vec)
{
  double x,y, result;
  
  x = y = 0.0;

  decode2(vec,x,y);

  result = 100*((x*x) - y)*((x*x) - y);
  result += ((1-x)*(1-x));

  return( 4000.0 - result);

}
void decode2( int *vec,double &x,double &y )
{
  int i;
  double k;
  
  x = y = 0.0;

  for (i = 1; i < 12; i++) {
    k = pow(2, i - 1);
    x += vec[i] * k;
    y += vec[12 + i] * k;
  }
  k = pow (2, 11);
  x -= vec[0] * k; x /= 1000.0;
  y -= vec[12] * k; y /= 1000.0;

  return;

}


// for De Jong function 3 's fitness evaluation
//   with the use CMAX = 30 for adjusment 
//       of the fitness
double eval3(int *vec)
{
  int i;
  double s;
  double x[5];

  for (i = 0; i < 5; i++)
    x[i] = 0.0;
  s = 0;
  
  decode3(vec,x);
  for (i = 0; i < 5; i++)
    s += x[i];
    
  return (30 - s);

}
void decode3( int *vec,double *x )
{
  int i, j;
  double k;

   for (i = 0; i < 5; i++)
    x[i] = 0.0;

  for (i = 1; i < 10; i++) {
    k = pow(2, i - 1);
    for (j = 0; j < 5; j++)
      x[j] += vec[j * 10 + i] * k;
  }
  k = pow(2, 9);
  for (j = 0; j < 5; j++) {
    x[j] -= vec[j * 10] * k;
    x[j] /= 100.0;
  }

  return;

}


// for De Jong function 4 's fitness evaluation
//   with the use CMAX = 1300.0 for adjusment 
//       of the fitness
double eval4(int *vec)
{
  int i;
  double result, randD, x[30];

  for(i=0;i<30;i++)
    x[i]=0;
  result = 0.0;
  
  decode4(vec,x);
  
  for(i = 0; i < 30; i++){
    randD= drand48();    
    if(rand() % 2)
      randD *= -1;   
    result += (i + 1) * pow(x[i],4) + randD;
  }
  //result += randD;
  return (1300.0 - result );

}
void decode4( int *vec,double *x)
{
  int i, j;
  double k;
    
  for (j = 0; j < 30; j++)
    x[j] = 0;

  for (i = 1; i < 8; i++) {
    k = pow(2, i - 1);
    for (j = 0; j < 30; j++)
      x[j] += vec[j * 8 + i] * k;
  }
  k = pow(2, 7);
  for (j = 0; j < 30; j++) {
    x[j] -= vec[j * 8] * k;
    x[j] /= 100.0;
  }

  return;

}
