/***********************************************************
 * File: main.cpp
 * Purpose:  This is the main loop that will maximize
 * a function eval
 *
 *
 * Date     Author    Comments
 * 09-02-04 TMorelli  Initial version
 **********************************************************/

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#define getrandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))

#define VECSIZE      50 

#define POPULATION   100
#define MUTATIONRATE 0.0001
#define XOVERRATE    0.001
#define ACC          1000
#define GENERATIONS  1000

using namespace std;

double eval(int *pj);

int FindMaxFitness (double * _fitness);
void CombineVecs(int * _vecA, int *_vecB);
void initVecs(int * _vec);
void DumpTopFitness(double _topFitness, unsigned long _count, 
                    unsigned long _topFitnessCount, int * _vec);
void FindMates(double * _fitness, int *_mates, double _avg);
void Decode(int * vec, unsigned long *a, unsigned long *b, unsigned long *c,
                       unsigned long *d, unsigned long *e);
void XOverMutate(int * _vecA, int * _vecB);
void FindFitness(double *_fitness, double * maxFitness, 
                 double *avgFitness, double *minFitness);

int main()
{
  int vec[POPULATION][VECSIZE];
  int tempVec[POPULATION][VECSIZE];
  int topVec[VECSIZE];
  double fitness[POPULATION], topFitness, avgFitness, minFitness;
  int mates[POPULATION]; 

 
  int i,z,y;
  unsigned long count = 0;
  unsigned long topFitnessCount=0;
  FILE * myFile;
  srand(time(NULL));
  topFitness = 0;
  
  for(i = 0; i < POPULATION; i++)
  { 
    initVecs(vec[i]);
  }
//  while (1)
 for (y = 0; y< 30; y++)
 {
  char buf[5];
  sprintf(buf, "./part1out/%d", y);
  printf("Opening: %s\n", buf);
  myFile = fopen(buf, "w"); 
  if (myFile == NULL)
    printf("Cannot open file\n");
  for ( z = 0; z < GENERATIONS; z++)
  {
    for (i=0; i < POPULATION; i++)
    {
      fitness[i] = eval(vec[i]);
    }
    int maxFitness = FindMaxFitness (fitness);
    memcpy(vec[POPULATION-1],vec[maxFitness],VECSIZE * sizeof(int)); 
    FindFitness(fitness, &topFitness, &avgFitness, &minFitness);
    FindMates(fitness, mates, avgFitness);
    for (int x = 0; x< POPULATION; x++)
    {
      memcpy(tempVec[x], vec[x], VECSIZE*sizeof(int));
    }
/*
    for (i = 0; i< POPULATION -1; i++)
    {
      CombineVecs(vec[i], tempVec[mates[i]]);
    }
*/
    for (i = 0; i < POPULATION - 1; i++)
    {
      if (getrandom(0,ACC) < XOVERRATE * ACC)
        XOverMutate(vec[i], tempVec[mates[i]]);
    }


    for (i=0; i < POPULATION; i++)
    {
      fitness[i] = eval(vec[i]);
    }
//    maxFitness = FindMaxFitness (fitness);
    FindFitness(fitness, &topFitness, &avgFitness, &minFitness);
 
    count++;    
//    topFitness = fitness[maxFitness];
    topFitnessCount = count;
//    if (count % 1000 == 0)
    { 
      //printf("%d\t%lf\t%lf\t%lf\n", count, topFitness, avgFitness, minFitness);
      fprintf(myFile, "%d\t%lf\t%lf\t%lf\n", z, topFitness, avgFitness, minFitness);
//      DumpTopFitness(topFitness, count, 
//                      topFitnessCount, vec[POPULATION-1]);
    }
  }
 fclose(myFile);
 }
}

void FindMates(double * _fitness, int *_mates, double _avg)
{
  double totalFitness = 0;
  int ranges[POPULATION];

  for (int x = 0; x< POPULATION-1; x++)
  {
    if (_fitness[x] > _avg)
      totalFitness += _fitness[x]*2;
    else
      totalFitness += _fitness[x];
    ranges[x] = (int)totalFitness;
  }
  for (int x = 0; x< POPULATION-1; x++)
  {
//Potential issue here person can mate with himself
    int location = getrandom(0,(int)totalFitness);
    _mates[x] = POPULATION-1;
    for (int y = 0; y< POPULATION -1; y++)
    {
      if (location <= ranges[y])
      {
        _mates[x] = y;
        break;
      }
    }
  }
}
void FindFitness(double *_fitness, double * maxFitness, 
                 double *avgFitness, double *minFitness)
{
  double total=0;

  *maxFitness = 0;
  *minFitness = 10000;
  *avgFitness = 0;

  for (int x = 0; x<POPULATION;x++)
  {
    if (_fitness[x] > *maxFitness)
      *maxFitness = _fitness[x];
    if (_fitness[x] < *minFitness)
      *minFitness = _fitness[x];
    total+= _fitness[x];

  }
  //printf("TOTAL: %lf\t%lf\n", total, total/POPULATION);
  *avgFitness = total/POPULATION;

}
int FindMaxFitness (double  * _fitness)
{
  double maxFitness = 0;
  int maxLoc = 0;
 
  for (int x = 0; x< POPULATION; x++)
  {
    if ( _fitness[x] > maxFitness)
    {
      maxFitness = _fitness[x];
      maxLoc = x;
    }
  }
  return maxLoc;
}

void XOverMutate(int * _vecA, int * _vecB)
{
  int x;
  int xOverPoint = getrandom(0, VECSIZE-1);
  for (x = 0; x < VECSIZE; x++)
  {
    if (x > xOverPoint)
      _vecA[x] = _vecB[x];

    if (!(getrandom(0,int(1/MUTATIONRATE))))
    {
      if (_vecA[x] == 0)
        _vecA[x] = 1;
      else
        _vecA[x] = 0;
    }
  }
}


void CombineVecs(int * _vecA, int *_vecB)
{
  int totalHits = 0;
  int hits[VECSIZE];

  for (int x = 0; x < VECSIZE; x++)
  {
    hits[x] = 0;
  }
  while (totalHits < (int) (VECSIZE / 2))
  {
    int temp = getrandom(0, VECSIZE-1);
    if (hits[temp] == 0)
    {
      hits[temp] = 1;
      totalHits ++;
    }
  }
  for (int x = 0; x< VECSIZE; x++)
  {
    if (hits[x])
    {
//      printf("Setting %d\n", x);
      _vecA[x] = _vecB[x];
    }
  }


/*
  int half = getrandom(0,1);
  if (half)
  {
    for (int x = 0; x< (int) (VECSIZE/2); x++)
    {
      _vecA[x] = _vecB[x];
    }
  }
  else
  {
    for (int x = (int)(VECSIZE/2); x< VECSIZE; x++)
    {
      _vecA[x] = _vecB[x];
    }
  }

*/
}



void initVecs(int * _vec)
{
  for (int x = 0; x < VECSIZE; x++)
  {
    _vec[x] = getrandom(0,1);
  }
}
void DumpTopFitness(double _topFitness, unsigned long _count, 
                    unsigned long _topFitnessCount, int * _vec)
{
  printf("Top Fitness after %ld iterations: %lf [%ld]\n", _count, _topFitness,
              _topFitnessCount);
  for(int i = 0; i < VECSIZE; i++)
    printf("%d ", _vec[i]);
  printf("\n");

}
double eval(int *pj)
{
  unsigned long a,b,c,d,e;
  double aa,bb,cc,dd,ee;
  double retval = 0;
  Decode(pj, &a,&b,&c,&d,&e);
  aa = (double) a;
  bb = (double) b;
  cc = (double) c;
  dd = (double) d;
  ee = (double) e;

  aa -=512;
  bb -=512;
  cc -=512;
  dd -=512;
  ee -=512;

  aa /=100;
  bb /=100;
  cc /=100;
  dd /=100;
  ee /=100;


//  printf("%ld %ld %ld\n", aa, bb, cc); 
  retval = (int)(aa) + (int)(bb) + (int)(cc) + (int)(dd)+(int)(ee);
  retval = 100 - retval;
  if (retval < 0)
    printf("ERROR\n");
  return retval;
}


void Decode(int * vec, unsigned long *a, unsigned long *b, unsigned long *c,
                       unsigned long *d, unsigned long *e)
{
  *a = *b = *c = *d = *e = 0;
 
  int x;
  for (x=9;x>=0;x--)
  {
    if (vec[x] == 1)
    {
      *a = (1<<(9-x)) | *a ;
    }
    if (vec[x+10] == 1)
    {
      *b |= (1<<((9-x)));
    }
    if (vec[x+20] == 1)
    {
      *c |= (1<<((9-x)));
    }
    if (vec[x+30] == 1)
    {
      *d |= (1<<((9-x)));
    }
    if (vec[x+40] == 1)
    {
      *e |= (1<<((9-x)));
    }

  }
}


