/*****************************************************************************
 * The chromosome in this example is an array of structures.  The structure  *
 * consists of an aray of six doubles and an array of forty ints.            *
 *****************************************************************************/
#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);
void *MyGetAllele(PGAContext *, int, int, int, int);
double MyEvaluate(PGAContext *, int, int);

typedef struct
{
     double first[6];
     int    second[40];
} MyStruct;

int main(int argc, char **argv)
{
     PGAContext *ctx;
     
     ctx = PGACreate       (&argc, argv, PGA_DATATYPE_USER, 5, PGA_MINIMIZE);
     PGASetRandomSeed      (ctx, 1);
     PGASetMaxIter         (ctx, 50);
     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);
     
     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 double to 0.0 and each int to 0 in each structure.           *
 *****************************************************************************/
void MyCreateString(PGAContext *ctx, int p, int pop, int InitFlag)
{
    int i, j, stringlen;
    MyStruct *struct_ptr;
    PGAIndividual *new;

    new = PGAGetIndividual(ctx, p, pop);
    stringlen = PGAGetStringLen(ctx);
    if (!(new->chrom = malloc (stringlen * sizeof(MyStruct))))
    {
	 fprintf(stderr, "No room for new->chrom.\n");
	 exit(1);
    }
    struct_ptr = (MyStruct *)new->chrom;
    if (InitFlag)
	 for (i = 0; i < stringlen; i++)
	 {
	      for (j = 0; j < 6; j++)
		   struct_ptr[i].first[j] = PGARandom01(ctx, 0) * 20.0 - 10.0;
	      for (j = 0; j < 40; j++)
		   struct_ptr[i].second[j] = PGARandomInterval(ctx, -20, 20);
	 }
    else
	 for (i = 0; i < stringlen; i++)
	 {
	      for (j = 0; j < 6; j++)
		   struct_ptr[i].first[j] = 0.0;
	      for (j = 0; j < 40; j++)
		   struct_ptr[i].second[j] = 0;
	 }
}

/*****************************************************************************
 * MyMutation performs mutation on a chromosome.  Each allele has a mr       *
 * probability of being changed.  Mutation here perturbs each double by one  *
 * tenth and each int by 1.                                                  *
 *****************************************************************************/
int MyMutation(PGAContext *ctx, int p, int pop, double mr)
{
     MyStruct *struct_ptr;
     int i, j, count = 0, stringlen;

     struct_ptr = (MyStruct *)PGAGetIndividual(ctx, p, pop)->chrom;
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
     {
	  if (PGARandomFlip(ctx, mr))
	  {
	       for (j = 0; j < 6; j++)
	       {
		    if (PGARandomFlip(ctx, 0.5))
			 struct_ptr[i].first[j] += 0.1*struct_ptr[i].first[j];
		    else
			 struct_ptr[i].first[j] -= 0.1*struct_ptr[i].first[j];
	       }
	       for (j = 0; j < 40; j++)
	       {
		    if (PGARandomFlip(ctx, 0.5))
			 struct_ptr[i].second[j] += 1;
		    else
			 struct_ptr[i].second[j] -= 1;
	       }
	       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, j, stringlen;
     MyStruct *parent1, *parent2, *child1, *child2;
     double pu;

     parent1 = (MyStruct *)PGAGetIndividual(ctx, p1, pop1)->chrom;
     parent2 = (MyStruct *)PGAGetIndividual(ctx, p2, pop1)->chrom;
     child1  = (MyStruct *)PGAGetIndividual(ctx, t1, pop2)->chrom;
     child2  = (MyStruct *)PGAGetIndividual(ctx, t2, pop2)->chrom;
     pu = PGAGetUniformCrossProb(ctx);
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
     {
	  for (j = 0; j < 6; j++)
	  {
	       if (parent1[i].first[j] == parent2[i].first[j])
	       {
		    child1[i].first[j] = parent1[i].first[j];
		    child2[i].first[j] = parent2[i].first[j];
	       }
	       else
	       {
		    if (PGARandomFlip(ctx, pu))
		    {
			 child1[i].first[j] = parent1[i].first[j];
			 child2[i].first[j] = parent2[i].first[j];
		    }
		    else
		    {
			 child1[i].first[j] = parent2[i].first[j];
			 child2[i].first[j] = parent1[i].first[j];
		    }
	       }
	  }
	  for (j = 0; j < 40; j++)
	  {
	       if (parent1[i].second[j] == parent2[i].second[j])
	       {
		    child1[i].second[j] = parent1[i].second[j];
		    child2[i].second[j] = parent2[i].second[j];
	       }
	       else
	       {
		    if (PGARandomFlip(ctx, pu))
		    {
			 child1[i].second[j] = parent1[i].second[j];
			 child2[i].second[j] = parent2[i].second[j];
		    }
		    else
		    {
			 child1[i].second[j] = parent2[i].second[j];
			 child2[i].second[j] = parent1[i].second[j];
		    }
	       }
	  }
     }
}

/*****************************************************************************
 * MyWriteString sends a visual representation of the chromosome to the file *
 * fp.                                                                       *
 *****************************************************************************/
void MyWriteString(PGAContext *ctx, FILE *fp, int p, int pop)
{
     MyStruct *struct_ptr;
     int i, j, stringlen;

     struct_ptr = (MyStruct *)PGAGetIndividual(ctx, p, pop)->chrom;
     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
     {
	  for (j = 0; j < 6; j++)
	       switch (j % 5)
	       {
	       case 0:
		    fprintf(fp, "#%5d: [%11.7g]", i, struct_ptr[i].first[j]);
		    break;
	       case 1:
	       case 2:
	       case 3:
		    fprintf(fp, ", [%11.7g]", struct_ptr[i].first[j]);
		    break;
	       case 4:
		    fprintf(fp, ", [%11.7g]", struct_ptr[i].first[j]);
		    if (j + 1 < 6)
			 fprintf(fp, "\n");
		    break;
	       }
	  fprintf(fp, "\n");
	  for (j = 0; j < 40; j++)
	       switch (j % 6)
	       {
	       case 0:
		    fprintf ( fp, "#%5d: [%9d]",i, struct_ptr[i].second[j]);
		    break;
	       case 1:
	       case 2:
	       case 3:
	       case 4:
		    fprintf(fp, ", [%9d]", struct_ptr[i].second[j]);
		    break;
	       case 5:
		    fprintf(fp, ", [%9d]", struct_ptr[i].second[j]);
		    if (j + 1 < 40)
			 fprintf(fp, "\n");
		    break;
	       }
	  fprintf(fp, "\n\n");
     }
     fprintf(fp, "\n");
}

/*****************************************************************************
 * 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)
{
     int stringlen;
     void *d, *s;

     s = PGAGetIndividual(ctx, p1, pop1)->chrom;
     d = PGAGetIndividual(ctx, p2, pop2)->chrom;
     stringlen = PGAGetStringLen(ctx);
     memcpy(d, s, stringlen * sizeof(MyStruct));
}

/*****************************************************************************
 * 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)
{
     int stringlen;
     void *a, *b;
     
     a = PGAGetIndividual(ctx, p1, pop1)->chrom;
     b = PGAGetIndividual(ctx, p2, pop2)->chrom;
     stringlen = PGAGetStringLen(ctx);
     return (!memcmp(a, b, stringlen * sizeof(MyStruct)));
}

/*****************************************************************************
 * MyGetAllele returns a pointer to the ith structure in the pth member of   *
 * population pop.                                                           *
 *****************************************************************************/
void *MyGetAllele(PGAContext *ctx, int p, int pop, int i, int size)
{
     PGAIndividual *ind;
     void *struct_ptr;

     ind = PGAGetIndividual(ctx, p, pop);
     struct_ptr = ind->chrom;
     return ((void *)((int)struct_ptr + size * i));
}

double MyEvaluate(PGAContext *ctx, int p, int pop)
{
     int i, j, k, stringlen, size;
     double first_sim = 0;
     int second_sim = 0;
     MyStruct **s;

     stringlen = PGAGetStringLen(ctx);
     size = sizeof(MyStruct);
     if (!(s = (MyStruct **) malloc (stringlen * sizeof(MyStruct *))))
     {
	  fprintf(stderr, "No room for s.\n");
	  exit(1);
     }
     for (i = 0; i < stringlen; i++)
	  s[i] = (MyStruct *)MyGetAllele(ctx, p, pop, i, size);
     for (i = 0; i < stringlen; i++)
     {
	  for (j = 0; j < stringlen; j++)
	  {
	       if (j != i)
	       {
		    for (k = 0; k < 6; k++)
			 first_sim += fabs(s[i]->first[k] - s[j]->first[k]);
		    for (k = 0; k < 40; k++)
			 second_sim += abs(s[i]->second[k] - s[j]->second[k]);
	       }
	  }
     }
     free(s);
     
     return ((double) second_sim + first_sim);
}
