/*****************************************************************************
 * The chromosome in this example is an array of 26 uppercase letters.  The  *
 * optimal solution is an array containing the alphabet in order.            *
 *****************************************************************************/
#include "pgapack.h"

void MyCreateString(PGAContext *, int, int, int);
int  MyMutation(PGAContext *, int, int, double);
void MyCrossover(PGAContext *, int, int, int, int, int, int);
void MyWriteString(PGAContext *, FILE *, int, int);
void MyCopyString(PGAContext *, int, int, int, int);
int  MyDuplicateString(PGAContext *, int, int, int, int);
double MyEvaluate(PGAContext *, int, int);

int main(int argc, char **argv)
{
     PGAContext *ctx;
     int maxiter;

     printf("How many iterations? ");
     scanf("%d", &maxiter);
     ctx = PGACreate       (&argc, argv, PGA_DATATYPE_USER, 26, PGA_MINIMIZE);
     PGASetRandomSeed      (ctx, 1);
     PGASetMaxIter         (ctx, maxiter);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_CREATESTRING,
			    (void *)MyCreateString);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_MUTATION,
			    (void *)MyMutation);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_CROSSOVER,
			    (void *)MyCrossover);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_WRITESTRING,
			    (void *)MyWriteString);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_COPYSTRING,
			    (void *)MyCopyString);
     PGASetUserFunction    (ctx, PGA_USERFUNCTION_DUPLICATE,
			    (void *)MyDuplicateString);
     PGASetMaxIter         (ctx, maxiter);
     PGASetUp              (ctx);
     PGARun                (ctx, MyEvaluate);
     PGADestroy            (ctx);
     return (0);
}

/*****************************************************************************
 * MyCreateString allocates and initializes a chromosome.  If InitFlag is    *
 * set to true, then it will randomly initialize the chromosome; otherwise,  *
 * it sets each character to '0'. Note that 0x41 and 0x5A are the hex ASCII  *
 * values for 'A' and 'Z' respectively.                                      *
 *****************************************************************************/
void MyCreateString(PGAContext *ctx, int p, int pop, int InitFlag)
{
     int i, stringlen;
     PGAIndividual *new;

     new = PGAGetIndividual(ctx, p, pop);
     if (!(new->chrom = malloc(26 * sizeof(char))))
     {
	  fprintf(stderr, "No room for new->chrom.\n");
	  exit(1);
     }
     stringlen = PGAGetStringLen(ctx);
     if (InitFlag)
	  for (i = 0; i < stringlen; i++)
	       ((char *)new->chrom)[i] = (char)PGARandomInterval(ctx, 'A',
								 'Z');
     else
	  for (i = 0; i < stringlen; i++)
	       ((char *)new->chrom)[i] = '0';
     
}

/*****************************************************************************
 * MyMutation performs mutation on a chromosome.  Each allele has a mr       *
 * probability of being changed.  Mutation here just replaces one character  *
 * with another randomly selected character.  Note that 0x41 and 0x5A are    *
 * the hex ASCII values for the letters 'A' and 'Z', respectively.           *
 *****************************************************************************/
int MyMutation(PGAContext *ctx, int p, int pop, double mr)
{
     char *string;
     int i, count = 0, stringlen;

     string = (char *)PGAGetIndividual(ctx, p, pop)->chrom;
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
	  if (PGARandomFlip(ctx, mr))
	  {
	       string[i] = (char) PGARandomInterval(ctx, 'A', 'Z');
	       count++;
	  }
     return (count);
}

/*****************************************************************************
 * MyCrossover implements uniform crossover on the chromosome.               *
 *****************************************************************************/
void MyCrossover(PGAContext *ctx, int p1, int p2, int pop1, int t1, int t2,
		 int pop2)
{
     int i, stringlen;
     char *parent1, *parent2, *child1, *child2;
     double pu;

     parent1 = (char *)PGAGetIndividual(ctx, p1, pop1)->chrom;
     parent2 = (char *)PGAGetIndividual(ctx, p2, pop1)->chrom;
     child1  = (char *)PGAGetIndividual(ctx, t1, pop2)->chrom;
     child2  = (char *)PGAGetIndividual(ctx, t2, pop2)->chrom;
     pu      = PGAGetUniformCrossProb(ctx);
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
	  if (parent1[i] == parent2[i])
	  {
	       child1[i] = parent1[i];
	       child2[i] = parent2[i];
	  }
	  else
	  {
	       if (PGARandomFlip(ctx, pu))
	       {
		    child1[i] = parent1[i];
		    child2[i] = parent2[i];
	       }
	       else
	       {
		    child1[i] = parent2[i];
		    child2[i] = parent1[i];
	       }
	  }
}
/*****************************************************************************
 * MyWriteString sends a visual representation of the chromosome to the file *
 * fp.                                                                       *
 *****************************************************************************/
void MyWriteString(PGAContext *ctx, FILE *fp, int p, int pop)
{
     char *string;

     string = (char *)PGAGetIndividual(ctx, p, pop)->chrom;
     fprintf(fp, "[%.26s]\n", string);
}

/*****************************************************************************
 * MyCopyString makes a copy of the chromosome at (p1, pop1) and puts it at  *
 * (p2, pop2).                                                               *
 *****************************************************************************/
void MyCopyString(PGAContext *ctx, int p1, int pop1, int p2, int pop2)
{
     void *d, *s;
    
     s = PGAGetIndividual(ctx, p1, pop1)->chrom;
     d = PGAGetIndividual(ctx, p2, pop2)->chrom;
     memcpy(d, s, 26 * sizeof(char));
}

/*****************************************************************************
 * MyDuplicateString compares two chromosomes and returns 1 if they are the  *
 * same and 0 if they are different.                                         *
 *****************************************************************************/
int MyDuplicateString(PGAContext *ctx, int p1, int pop1, int p2, int pop2)
{
     void *a, *b;

     a = PGAGetIndividual(ctx, p1, pop1)->chrom;
     b = PGAGetIndividual(ctx, p2, pop2)->chrom;
     return (!memcmp(a, b, 26 * sizeof(char)));
}

double MyEvaluate(PGAContext *ctx, int p, int pop)
{
     int i, stringlen, counter[26];
     PGAIndividual *ind;
     char *string;
     int term1 = 0, term2 = 0, term3 = 0;

     ind = PGAGetIndividual(ctx, p, pop);
     string = (char *)ind->chrom;
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
	  counter[i] = 0;
     for (i = 0; i < stringlen - 1; i++)
     {
	  term1 += (string[i+1] - string[i]);
	  if (string[i+1] - string[i] < 0)
	       term2 += stringlen * (string[i] - string[i+1]);
	  counter[string[i] - 0x41]++;
     }
     counter[string[stringlen-1] - 0x41]++;
     for (i = 0; i < stringlen; i++)
	  term3 += stringlen * abs(counter[i] - 1);
     return (double)(term1 + term2 + term3);
}
