/*****************************************************************************
 * This is an example program for PGAPack.  The objective is to maximize the *
 * number of 1-bits in a chromosome.  A user-supplied hill-climbing          *
 * heuristic, setbit, is called that randomly selects a bit to set to one    *
 *****************************************************************************/
#include "pgapack.h"

void SelectOptionsByMenu(PGAContext *);
double evaluate(PGAContext *, int, int);
void setbit(PGAContext *, int);

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

     ctx = PGACreate(&argc, argv, PGA_DATATYPE_BINARY, 100, PGA_MAXIMIZE);
     SelectOptionsByMenu(ctx);
     PGASetRandomSeed(ctx, 1);
     PGASetUp(ctx);
     PGAEvaluate(ctx, PGA_OLDPOP, (*evaluate));  
     PGAFitness(ctx, PGA_OLDPOP);
     monc = PGAGetMutateOnlyNoCross(ctx);
     while(!PGADone(ctx))
     {
	  PGASelect( ctx, PGA_OLDPOP);
	  if (monc == PGATRUE)
	       PGARunMutateOrCross(ctx, PGA_OLDPOP, PGA_NEWPOP);
	  else
	       PGARunMutateAndCross(ctx, PGA_OLDPOP, PGA_NEWPOP);
	  setbit(ctx, PGA_NEWPOP);
	  PGAEvaluate(ctx, PGA_NEWPOP, (*evaluate));
	  PGAFitness(ctx, PGA_NEWPOP);
	  PGAUpdateGeneration(ctx);
	  PGAPrintReport(ctx, stdout, PGA_OLDPOP);
     }
     PGADestroy(ctx);
     return (0);
}

/*******************************************************************
*               hill-climber                                       *
*******************************************************************/
void setbit(PGAContext *ctx, int pop)
{
     int i, p;
    
     int popsize;
     int stringlen;

     popsize   = PGAGetPopSize(ctx);
     stringlen = PGAGetStringLen(ctx);
     for (p=0; p<popsize; p++)
     {
	  i = PGARandomInterval(ctx, 0, stringlen-1);
	  PGASetBinaryAllele(ctx, p, pop, i, 1);
	  PGASetEvalUpToDate(ctx, p, pop, PGAFALSE);
     }
}

/*******************************************************************
*               user defined evaluation function                   *
*   ctx - contex variable                                          *
*   p   - chromosome index in population                           *
*   pop - which population to refer to                             *
*******************************************************************/
double evaluate (PGAContext *ctx, int p, int pop)
{
     int i, nbits = 0, stringlen;

     stringlen = PGAGetStringLen(ctx);
     for (i = 0; i < stringlen; i++)
	  if (PGAGetBinaryAllele(ctx, p, pop, i))
	       nbits++;

     return((double) nbits);
}

/*******************************************************************
*               user menu, calls PGACreate and returns ctx         *
*******************************************************************/
void SelectOptionsByMenu(PGAContext *ctx)
{
     char c;
     int s, choose, quit = 0,
     pop = 100,
     stp = 0,
     max = 1000,
     num = 10, 
     cross = PGA_CROSSOVER_TWOPT,
     sel = PGA_SELECT_TOURNAMENT,
     fit = PGA_FITNESS_RAW,
     fitmin = PGA_FITNESSMIN_CMAX,
     mut = PGA_MUTATION_STRINGLEN,
     mutonly = PGATRUE,
     nodup = PGAFALSE,
     poprep = PGA_POPREPL_BEST,
     freq = 10,
     mask,
     max_no_ch = 50,
     maxsim = 95,
     pr_opts = 0;
     double mprob = 0.001,
     cprob = 0.85,
     ucprob = 0.6,
     frmax = 1.2;
     
     do
     {
	  system("clear");
	  choose = -1;
	  printf("\nParameters:\n\n");
	  printf(  "     1. Population size:           %d\n", pop);
	  printf(  "     2. Stopping rule:\n");
	  for (mask = PGA_STOP_MAXITER; mask <= PGA_STOP_TOOSIMILAR;
	       mask <<= 1)
	       if ((stp & mask) == mask)
		    switch (mask)
		    {
		    case PGA_STOP_MAXITER:
			 printf("\t\tMaximum iterations (%d)\n", max);
			 break;
		    case PGA_STOP_NOCHANGE:
			 printf("\t\tNo change after %d iterations\n",
				max_no_ch);
			 break;
		    case PGA_STOP_TOOSIMILAR:
			 printf("\t\tToo similar (%d%%)\n", maxsim);
			 break;
		    default:
			 break;
		    }
	  printf(  "     3. Number to replace:         %d\n", num);
	  printf(  "     4. Crossover type:            ");
	  switch (cross)
	  {
	  case PGA_CROSSOVER_ONEPT: 
	       printf("One point\n");
	       break;
	  case PGA_CROSSOVER_TWOPT:
	       printf("Two point\n");
	       break;
	  case PGA_CROSSOVER_UNIFORM:
	       printf("Uniform\n");
	       break;
	  default:
	       break;
	  }
	  printf(  "     5. Select type:               ");
	  switch (sel)
	  {
	  case PGA_SELECT_PROPORTIONAL:
	       printf("Proportional selection\n");
	       break;
	  case PGA_SELECT_SUS:
	       printf("Stochastic universal selection\n");
	       break;
	  case PGA_SELECT_TOURNAMENT:
	       printf("Tournament\n");
	       break;
	  case PGA_SELECT_PTOURNAMENT:
	       printf("Probabilistic tournament\n");
	       break;
	  default:
	       break;
	  }
	  printf(  "     6. Fitness type:              ");
	  switch (fit)
	  {
	  case PGA_FITNESS_RAW:
	       printf("Raw fitness\n");
	       break;
	  case PGA_FITNESS_NORMAL:
	       printf("Normal\n");
	       break;
	  case PGA_FITNESS_RANKING:
	       printf("Ranking\n");
	       break;
	  default:
	       ;
	  }
	  printf(  "     7. Fitness minimization type: ");
	  switch (fitmin)
	  {
	  case PGA_FITNESSMIN_RECIPROCAL:
	       printf("Reciprocal\n");
	       break;
	  case PGA_FITNESSMIN_CMAX:
	       printf("C Max\n");
	       break;
	  default:
	       break;
	  }
	  printf(  "     8. Mutation type:             ");
	  switch (mut)
	  {
	  case PGA_MUTATION_FIXED:
	       printf("Fixed\n");
	       break;
	  case PGA_MUTATION_STRINGLEN:
	       printf("String length\n");
	       break;
	  case PGA_MUTATION_HAMMING:
	       printf("Hamming distance\n");
	       break;
	  case PGA_MUTATION_UNIFORM:
	       printf("Uniform\n");
	       break;
	  case PGA_MUTATION_GAUSSIAN:
	       printf("Gaussian\n");
	       break;
	  default:
	       printf("Invalid Mutation type: %d", mut);
	       break;
	  }
	  printf(  "     9. Mutate only (no cross):    ");
	  if (mutonly == PGATRUE)
	       printf("True\n");
	  else
	       printf("False\n");
	  printf(  "    10. No duplicates:             ");
	  if (nodup == PGATRUE)
	       printf("True\n");
	  else
	       printf("False\n");
	  printf(  "    11. Mutation probability:      %f\n", mprob);
	  printf(  "    12. Crossover probability:     ");
	  if (cross == PGA_CROSSOVER_UNIFORM)
	       printf("%f\n", ucprob);
	  else
	       printf("%f\n", cprob);
	  printf(  "    13. Fitness rank maximum:      %f\n", frmax);
	  printf(  "    14. Population replacement:    ");
	  switch (poprep)
	  {
	  case PGA_POPREPL_BEST:
	       printf("Best\n");
	       break;
	  case PGA_POPREPL_RANDOM_NOREP:
	       printf("Random without replacement\n");
	       break;
	  case PGA_POPREPL_RANDOM_REP:
	       printf("Random with replacement\n");
	       break;
	  default:
	       break;
	  }
	  printf(  "    15. Print Frequency:           %d\n", freq);
	  printf(  "    16. Print Options:\n");
	  for (mask = PGA_ONLINE; mask <= PGA_WORST; mask <<= 1)
	       if ((pr_opts & mask) == mask)
		    switch (mask)
		    {
		    case PGA_ONLINE:
			 printf("\t\tOnline analysis\n");
			 break;
		    case PGA_OFFLINE:
			 printf("\t\tOffline analysis\n");
			 break;
		    case PGA_HAMMING:
			 printf("\t\tHamming distance\n");
			 break;
		    case PGA_STRING:
			 printf("\t\tBest string\n");
			 break;
		    case PGA_WORST:
			 printf("\t\tWorst evaluation\n");
			 break;
		    default:
			 printf("Invalid report option: %d\n", mask);
			 break;
		    }
	  printf("\nSelect number to change, q to quit: ");
	  scanf("\n%d", &choose);
	  printf("\n\n");
	  switch (choose)
	  {
	  case -1: 
	       scanf("%c",&c);
	       if ((c == 'q') || (c == 'Q'))
		    quit = 1;
	       break;
	  case 1:
	       pop = 0;		
	       do
	       {
		    s = 0;
		    system("clear");
	    	    printf("\nPopulation size: ");
    	    	    s = scanf("\n%d", &pop);
	    	    if (s <= 0)
			 scanf("%c", &c);
	       } while (pop < 1);
	       if (num > pop)
		    num = pop;
	       break;
	  case 2:
	       do
	       {
		    system("clear");
		    printf("\nChoose stopping rule:\n\n");
		    printf("    1. Maximum iterations\n");
		    printf("    2. No change\n");
		    printf("    3. Too similar\n");
		    printf("\n  ->");
		    scanf("\n%d", &s);
	       } while ((s < 1) || (s > 3));
	       switch (s)
	       {
	       case 1:
		    do
		    {
			 printf("\nMaximum iterations: ");
			 scanf("\n%d", &max);
		    } while (max < 1);
		    stp |= PGA_STOP_MAXITER;
		    PGASetStoppingRule(ctx, PGA_STOP_MAXITER);
		    break;
	       case 2:
		    do
		    {
			 printf("\nMaximum iterations with no change: ");
			 scanf("\n%d", &max_no_ch);
		    } while (max_no_ch < 2);
		    stp |= PGA_STOP_NOCHANGE;
		    PGASetStoppingRule(ctx, PGA_STOP_NOCHANGE);
		    break;
	       case 3:
		    do
		    {
			 printf("\nPercent similarity: ");
			 scanf("\n%d", &maxsim);
		    } while (maxsim < 1);
		    stp |= PGA_STOP_TOOSIMILAR;
		    PGASetStoppingRule(ctx, PGA_STOP_TOOSIMILAR);
		    break;
	       default:
		    printf("Invalid stopping rule: %d", s);
		    break;
	       }  
	       break;
	  case 3:
	       num = 0;		
	       do
	       {
		    s = 0;
		    system("clear");
	    	    printf("\nNumber to replace (1 - %d): ", pop);
    	    	    s = scanf("\n%d", &num);
	    	    if (s <= 0)
			 scanf("%c", &c);
	       } while ((num < 1) || (num > pop));
	       break;
	  case 4:
	       cross = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose crossover type:\n\n");
		    printf("    1. One point\n");
		    printf("    2. Two point\n");
		    printf("    3. Uniform\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &cross);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((cross < 1) || (cross > 3));
	       break;
	  case 5:
	       sel = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose select type:\n\n");
		    printf("    1. Proportional selection\n");
		    printf("    2. Stochastic universal selection\n");
		    printf("    3. Tournament\n");
		    printf("    4. Probabilistic tournament\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &sel);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((sel < 1) || (sel > 4));
	       break;
	  case 6:
	       fit = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose fitness type:\n\n");
		    printf("    1. Raw fitness\n");
		    printf("    2. Normal\n");
		    printf("    3. Ranking\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &fit);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((fit < 1) || (fit > 3));
	       break;
	  case 7:
	       fitmin = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose fitness type:\n\n");
		    printf("    1. Reciprocal\n");
		    printf("    2. C max\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &fitmin);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((fitmin < 1) || (fitmin > 2));
	       break;
	  case 8:
	       mut = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose mutation type:\n\n");
		    printf("    1. Fixed\n");
		    printf("    2. String length\n");
		    printf("    3. Hamming distance\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &mut);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((mut < 1) || (mut > 3));
	       break;
	  case 9:
	       mutonly = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nMutate only (no cross)?:\n\n");
		    printf("    1. True\n");
		    printf("    2. False\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &mutonly);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((mutonly < 1) || (mutonly > 2));
	       if (mutonly == 2)
		    mutonly = 0;
	       break;
	  case 10:
	       nodup = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nNo duplicates?:\n\n");
		    printf("    1. True\n");
		    printf("    2. False\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &nodup);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((nodup < 1) || (nodup > 2));
	       if (nodup == 2)
		    nodup = 0;
	       break;
	  case 11:
	       mprob = -1.0;		
	       do
	       {
		    s = 0;
		    system("clear");
	    	    printf("\nMutation probability: ");
    	    	    s = scanf("\n%lf", &mprob);
	    	    if (s <= 0)
			 scanf("%c", &c);
	       } while (mprob < 0.0);
	       break;   
	  case 12:
	       if (cross == PGA_CROSSOVER_UNIFORM)
	       {
		    ucprob = -1.0;		
		    do
		    {
			 s = 0;
			 system("clear");
			 printf("\nCrossover probability: ");
			 s = scanf("\n%lf", &ucprob);
			 if (s <= 0)
			      scanf("%c", &c);
		    } while (ucprob < 0.0);
	       }
	       else
	       {
		    cprob = -1.0;		
		    do
		    {
			 s = 0;
			 system("clear");
			 printf("\nCrossover probability: ");
			 s = scanf("\n%lf", &cprob);
			 if (s <= 0)
			      scanf("%c", &c);
		    } while (cprob < 0.0);
	       }
	       break;
	  case 13:
	       frmax = -1.0;		
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nFitness rank maximum: ");
		    s = scanf("\n%lf", &frmax);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while (frmax < 0.0);
	       break;
	  case 14:
	       poprep = 0;
	       do
	       {
		    s = 0;
		    system("clear");
		    printf("\nChoose population replacement:\n\n");
		    printf("    1. Best\n");
		    printf("    2. Random without replacement\n");
		    printf("    3. Random with replacement\n");
		    printf("\n  ->");
		    s = scanf("\n%d", &poprep);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while ((poprep < 1) || (poprep > 3));
	       break;
	    case 15:
	       freq = 0;		
	       do
	       {
		    s = 0;
		    printf("\nPrint frequency: ");
		    s = scanf("\n%d", &freq);
		    if (s <= 0)
			 scanf("%c", &c);
	       } while (freq < 0);
	       break;
	  case 16:
	       do
	       {
		    system("clear");
		    printf("\nChoose report option:\n\n");
		    printf("    1. Online analysis\n");
		    printf("    2. Offline analysis\n");
		    printf("    3. Hamming distance\n");
		    printf("    4. Best string\n");
		    printf("    5. Worst evaluation\n");
		    scanf("\n%d", &s);
	       } while (s < 1 || s > 5);
	       switch (s)
	       {
	       case 1:
		    pr_opts |= PGA_ONLINE;
		    PGASetPrintOptions(ctx, PGA_ONLINE);
		    break;
	       case 2:
		    pr_opts |= PGA_OFFLINE;
		    PGASetPrintOptions(ctx, PGA_OFFLINE);
		    break;
	       case 3:
		    pr_opts |= PGA_HAMMING;
		    PGASetPrintOptions(ctx, PGA_HAMMING);
		    break;
	       case 4:
		    pr_opts |= PGA_STRING;
		    PGASetPrintOptions(ctx, PGA_STRING);
		    break;
	       case 5:
		    pr_opts |= PGA_WORST;
		    PGASetPrintOptions(ctx, PGA_WORST);
		    break;
	       default:
		    printf("Invalid report option\n");
		    break;
	       }
	       break;
	  default:
	       break;
	  }
     } while (!quit);
     PGASetPopSize(ctx, pop);
     PGASetMaxIter(ctx, max);
     PGASetMaxNoChange(ctx, max_no_ch);
     PGASetMaxSimilarity(ctx, maxsim);
     PGASetNumReplace(ctx, num);
     PGASetCrossoverType(ctx, cross);
     PGASetSelectType(ctx, sel);
     PGASetFitnessType(ctx, fit);
     PGASetFitnessMinType(ctx, fitmin);
     PGASetMutationType(ctx, mut);
     PGASetMutateOnlyNoCross(ctx, mutonly);
     PGASetNoDuplicates(ctx, nodup);
     if (mut != PGA_MUTATION_STRINGLEN && mut != PGA_MUTATION_HAMMING)
	  PGASetMutationProb(ctx, mprob);
     PGASetCrossoverProb(ctx, cprob);
     PGASetUniformCrossProb(ctx, ucprob);
     PGASetFitnessRankMax(ctx, frmax);
     PGASetPopReplacement(ctx, poprep);
     PGASetPrintFreq(ctx, freq);
}
