/*
 * genetic.c
 *
 *  Created on: Nov 23, 2009
 *      Author: brian
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "genetic.h"
#include "constants.h"


double fitness(int chr[], int len, int adjTable[][N_VERTICES])
{
	int i, j;

	//Chromosomes get 2 points for having different colors
	//Lose 1 point for having the same color
	int points = 0;

	//number of distinct colors used
	int ncolors = 0;

	//Only use half of the adj table
	for (i = 1; i < len; i++)
	{
		for (j = (i + 1); j < len; j++)
		{
			//Test if vertex i and j are connected
			if (adjTable[i][j] != 0)
			{
				//Test if their colors are the same
				if (chr[i] == chr[j])
				{
					//Colors are the same
					//printf("Vertex %d and %d have same color (%d)\n", i, j, chr[i]);
					points -= 4;
				}
				else
				{
					//Colors are different
					//printf("Vertex %d and %d have different colors (%d and %d)\n", i, j, chr[i], chr[j]);
					points += 1;
				}
			}
		}
	}

	//Count the number of colors used

	ncolors = countColors(chr);


	return ((double) points) / (sqrt((double) ncolors));
}

void randChr(int chr[], int size)
{
	int i;

	for (i = 0; i < size; i++)
	{
		chr[i] = randInt(size);
	}
}

void printChr(char str[], int chr[], int size)
{
	int i;
	int len = 0;
	len += sprintf(str, "{ ");
	for (i = 0; i < size; i++)
	{
		len += sprintf(str + len, "%d, ", chr[i]);
	}
	sprintf(str + len, "}\n");
}

//Return a random int (0 <= randInt < n)
int randInt(int n)
{
	return (rand() % n);
}

//return a random double in the range 0...d
double randDouble(double d)
{
	return ((double) rand()) / ((double) RAND_MAX) * d;
}

void copyChr(int src[], int dest[], int len)
{
	int i;

	for (i = 0; i < len; i++)
	{
		dest[i] = src[i];
	}
}

int countColors(int chr[])
{
	int len = N_VERTICES;
	int colors_used[N_VERTICES];
	int ncolors = 0;
	int i;
	//Count the number of colors used

		//First, initialize the colors_used array to zero
		for (i = 0; i < len; i++)
		{
			colors_used[i] = 0;
		}

		//Then, go through the chromosome and record the colors used
		for (i = 0; i < len; i++)
		{
			//Check for a valid color (0 <= color < N_VERTICES)
			if ((chr[i] < 0) || (chr[i] >= N_VERTICES))
			{
				//BAD
				printf("Error: %d is not a valid color for %d nodes", chr[i], N_VERTICES);
				//Exit
				exit(-1);
			}

			colors_used[chr[i]]++;
		}

		//Then determine the number of unique colors used
		for (i = 0; i < len; i++)
		{
			if (colors_used[i] != 0)
			{
				ncolors++;
			}
		}

		return ncolors;
}

//Prescale the fitness scores in place
void prescaleFitness(double rawfitness[], int popsize)
{
	//Find the smallest fitness value
	double smallest = rawfitness[0];
	int i;

	for (i = 1; i < popsize; i++)
	{
		if (rawfitness[i] < smallest)
		{
			smallest = rawfitness[i];
		}
	}

	//Subtract this from all fitness values
	for (i = 0; i < popsize; i++)
	{
		rawfitness[i] = rawfitness[i] - smallest;
	}

	//This shifts the fitness scores so the smallest score is zero
}

double scaleHigh(double prescaledFitness[], double highFitness[], int popsize)
{
	int i;
	double largest = prescaledFitness[0];
	double sum = 0;

	//Find the maximum fitness
	for (i = 1; i < popsize; i++)
	{
		if (prescaledFitness[i] > largest)
		{
			largest = prescaledFitness[i];
		}
	}

	//Scale each fitness score using Eq. 8.10 in Engelbrecht
	for (i = 0; i < popsize; i++)
	{
		highFitness[i] = 1.0 / (1 + largest - prescaledFitness[i]);
		sum += highFitness[i];
	}

	//Return the sum of the scaled fitness scores
	return sum;
}

double scaleLow(double prescaledFitness[], double lowFitness[], int popsize)
{
	int i;
	double smallest = prescaledFitness[0];
	double sum = 0;

	//Find the maximum fitness
	for (i = 1; i < popsize; i++)
	{
		if (prescaledFitness[i] < smallest)
		{
			smallest = prescaledFitness[i];
		}
	}

	//Scale each fitness score using Eq. 8.10 in Engelbrecht
	for (i = 0; i < popsize; i++)
	{
		lowFitness[i] = 1.0 / (1 - smallest + prescaledFitness[i]);
		sum += lowFitness[i];
	}

	//Return the sum of the scaled fitness scores
	return sum;
}

int propSel(double scaledFitness[], double scaledFitnessSum, int popsize)
{
	double sum = scaledFitness[0];
	int i = 0;

	double r = randDouble(scaledFitnessSum);

	while ((sum < r) && (i < popsize))
	{
		i++;
		sum += scaledFitness[i];
	}

	return i;
}

//Perform one point crossover
//p1 and p2 produce children c1 and c2
//len is the size of the chromosome
void onePoint(int p1[], int p2[], int c1[], int c2[], int len)
{
	//Generate crossover point
	int cp = randInt(len);

	int i;

	//Generate the children
	for (i = 0; i < len; i++)
	{
		if (i < cp)
		{
			//Before the crossover point
			c1[i] = p1[i];
			c2[i] = p2[i];
		}
		else
		{
			//After the crossover point
			c1[i] = p2[i];
			c2[i] = p1[i];
		}
	}
}


//Perform two point crossover
void twoPoint(int p1[], int p2[], int c1[], int c2[], int len)
{
	//Generate the two crossover points
	int cp1 = randInt(len);
	int cp2 = randInt(len);

	//Make sure that they are in order and not equal
	while ((cp1 > cp2) || (cp1 == cp2))
	{
		//pick new crossover points until they are satisfactory
		cp1 = randInt(len);
		cp2 = randInt(len);
	}

	int i;

	for (i = 0; i < cp1; i++)
	{
		//Before the 1st crossover point
		c1[i] = p1[i];
		c2[i] = p2[i];
	}

	for (i = cp1; i < cp2; i++)
	{
		//Between the two crossover points
		c1[i] = p2[i];
		c2[i] = p1[i];
	}

	for(i = cp2; i < len; i++)
	{
		//After both crossover points
		c1[i] = p1[i];
		c2[i] = p2[i];
	}
}

//Perform uniform crossover
void uniform(int p1[], int p2[], int c1[], int c2[], int len)
{
	int i;
	double r;

	for (i = 0; i < len; i++)
	{
		//Randomly select each gene from one parent or the other
		r = randDouble(1.0);

		if (r < 0.5)
		{
			c1[i] = p1[i];
			c2[i] = p2[i];
		}
		else
		{
			c1[i] = p2[i];
			c2[i] = p1[i];
		}
	}
}

int loadAdjTable(char filename[], int size, int adjTable[][N_VERTICES])
{
	FILE* inFile;
	int i,j;


	//Open the file
	if ((inFile = fopen(filename, "r")) == NULL)
	{
		printf("ERROR: Couldn't open file %s", filename);
		return -1;
	}

	//Load the adjTable from a file
	for (i = 0; i < N_VERTICES; i++)
	{
		for (j = 0; j < N_VERTICES; j++)
		{
			fscanf(inFile, "%d", &(adjTable[i][j]));
		}
	}

	fclose(inFile);
	return 0;
}

void mutate(int chromosome[], int size, double p_mutate)
{
	double r;
	int i;

	for (i = 0; i < size; i++)
	{
		r = randDouble(1.0);

		if (r < p_mutate)
		{
			//Randomly pick 1 or -1
			int n = randInt(2);

			if (n == 0)
			{
				//Picked -1
				chromosome[i] = chromosome[i] - 1;
			}
			else
			{
				//Picked 1
				chromosome[i] = chromosome[i] + 1;
			}

			//Handle any out of range gene value by wrapping around
			if (chromosome[i] < 0)
			{
				chromosome[i] = size - 1;
			}
			chromosome[i] = chromosome[i] % size;
		}
}
}
