/*****************************************************************************
 * The chromosome in this example is a structure containing three doubles    *
 * representing a translation along each of the x-, y-, and z-axes; three    *
 * doubles representing a rotation about each the axes; and forty ints       *
 * representing an index to side chain rotations.  This is a simple          *
 * illustration of how to set up a molecular docking problem.                *
 *****************************************************************************/
#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);

typedef struct
{
     double t[3];
     double r[3];
     int    sc[40];
} MyStruct;

int main(int argc, char **argv)
{
     PGAContext *ctx;
     
     ctx = PGACreate       (&argc, argv, PGA_DATATYPE_USER, 46, 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.                             *
 *****************************************************************************/
void MyCreateString(PGAContext *ctx, int p, int pop, int InitFlag)
{
     int i;
     MyStruct *struct_ptr;
     PGAIndividual *new;

     new = PGAGetIndividual(ctx, p, pop);
     if (!(new->chrom = malloc(sizeof(MyStruct))))
     {
	  fprintf(stderr, "No room for new->chrom");
	  exit(1);
     }
     struct_ptr = (MyStruct *)new->chrom;
     if (InitFlag)
     {
	  for (i = 0; i < 3; i++)
	       struct_ptr->t[i] = PGARandom01(ctx, 0) * 20.0 - 10.0;
	  for (i = 0; i < 3; i++)
	       struct_ptr->r[i] = PGARandom01(ctx, 0) * 20.0 - 10.0;
	  for (i = 0; i < 40; i++)
	       struct_ptr->sc[i] = PGARandomInterval(ctx, -20, 20);
     }
     else
     {
	  for (i = 0; i < 3; i++)
	       struct_ptr->t[i] = 0.0;
	  for (i = 0; i < 3; i++)
	       struct_ptr->r[i] = 0.0;
	  for (i = 0; i < 40; i++)
	       struct_ptr->sc[i] = 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, count = 0;

     struct_ptr = (MyStruct *)PGAGetIndividual(ctx, p, pop)->chrom;
     for (i = 0; i < 3; i++)
	  if (PGARandomFlip(ctx, mr))
	  {
	       if (PGARandomFlip(ctx, 0.5))
		    struct_ptr->t[i] += 0.1*struct_ptr->t[i];
	       else
		    struct_ptr->t[i] -= 0.1*struct_ptr->t[i];
	       count++;
	  }
     for (i = 0; i < 3; i++)
	  if (PGARandomFlip(ctx, mr))
	  {
	       if (PGARandomFlip(ctx, 0.5))
		    struct_ptr->r[i] += 0.1*struct_ptr->r[i];
	       else
		    struct_ptr->r[i] -= 0.1*struct_ptr->r[i];
	       count++;
	  }
     for (i = 0; i < 40; i++)
	  if (PGARandomFlip(ctx, mr))
	  {
	       if (PGARandomFlip(ctx, 0.5))
		    struct_ptr->sc[i] += 1;
	       else
		    struct_ptr->sc[i] -= 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;
     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);
     for (i = 0; i < 3; i++)
	  if (parent1->t[i] == parent2->t[i])
	  {
	       child1->t[i] = parent1->t[i];
	       child2->t[i] = parent2->t[i];
	  }
	  else
	  {
	       if (PGARandomFlip(ctx, pu))
	       {
		    child1->t[i] = parent1->t[i];
		    child2->t[i] = parent2->t[i];
	       }
	       else
	       {
		    child1->t[i] = parent2->t[i];
		    child2->t[i] = parent1->t[i];
	       }
	  }
     for (i = 0; i < 3; i++)
	  if (parent1->r[i] == parent2->r[i])
	  {
	       child1->r[i] = parent1->r[i];
	       child2->r[i] = parent2->r[i];
	  }
	  else
	  {
	       if (PGARandomFlip(ctx, pu))
	       {
		    child1->r[i] = parent1->r[i];
		    child2->r[i] = parent2->r[i];
	       }
	       else
	       {
		    child1->r[i] = parent2->r[i];
		    child2->r[i] = parent1->r[i];
	       }
	  }
     for (i = 0; i < 40; i++)
	  if (parent1->sc[i] == parent2->sc[i])
	  {
	       child1->sc[i] = parent1->sc[i];
	       child2->sc[i] = parent2->sc[i];
	  }
	  else
	  {
	       if (PGARandomFlip(ctx, pu))
	       {
		    child1->sc[i] = parent1->sc[i];
		    child2->sc[i] = parent2->sc[i];
	       }
	       else
	       {
		    child1->sc[i] = parent2->sc[i];
		    child2->sc[i] = parent1->sc[i];
	       }
	  }
}

/*****************************************************************************
 * 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;

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

     for(i = 0; i < 3; i++)
     {
	  switch ( i % 5 )
	  {
	  case 0:
	       fprintf ( fp, "#%5d: [%11.7g]",i,struct_ptr->r[i]);
	       break;
	  case 1:
	  case 2:
	  case 3:
	       fprintf ( fp, ", [%11.7g]",struct_ptr->r[i]);
	       break;
	  case 4:
	       fprintf ( fp, ", [%11.7g]",struct_ptr->r[i]);
	       if (i+1 < 3)
		    fprintf ( fp, "\n");
	       break;
	  }
     }
     fprintf ( fp, "\n" );

     for(i = 0; i < 40; i++)
     {
	  switch ( i % 6 )
	  {
	  case 0:
	       fprintf ( fp, "#%5d: [%9ld]",i,(long)struct_ptr->sc[i]);
	       break;
	  case 1:
	  case 2:
	  case 3:
	  case 4:
	       fprintf ( fp, ", [%9ld]",(long)struct_ptr->sc[i]);
	       break;
	  case 5:
	       fprintf ( fp, ", [%9ld]",(long)struct_ptr->sc[i]);
	       if (i+1 < 40)
		    fprintf ( fp, "\n");
	       break;
	  }
     }
     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)
{
     void *d, *s;

     s = PGAGetIndividual(ctx, p1, pop1)->chrom;
     d = PGAGetIndividual(ctx, p2, pop2)->chrom;
     memcpy(d, s, 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)
{
     void *a, *b;

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

double MyEvaluate(PGAContext *ctx, int p, int pop)
{
     int i, j;
     double t_sim = 0.0, r_sim = 0.0;
     int sc_sim = 0;
     PGAIndividual *ind;
     MyStruct conf;

     ind = PGAGetIndividual(ctx, p, pop);
     conf = *((MyStruct *)ind->chrom);
     for (i = 0; i < 3; i++)
	  for (j = 0; j < 3; j++)
	       if (j != i)
		    t_sim += fabs(conf.t[i] - conf.t[j]);
     for (i = 0; i < 3; i++)
	  for (j = 0; j < 3; j++)
	       if (j != i)
		    r_sim += fabs(conf.r[i] - conf.r[j]);
     for (i = 0; i < 40; i++)
	  for (j = 0; j < 40; j++)
	       if (j != i)
		    sc_sim += abs(conf.sc[i] - conf.sc[j]);
     return (t_sim + r_sim + (double)sc_sim);
}
