/*****************************************************************************
*     FILE: pga.c: This file contains all the routines that are data structure
*                  neutral
*
*     Authors: David M. Levine, Philip L. Hallstrom, and David M. Noelle
*****************************************************************************/

#include "pgapack.h"

/*U****************************************************************************
  PGARun - High-level routine to execute the genetic algorithm.  It is called
  after PGACreate and PGASetup have been called.

  Category: Generation

  Inputs:
    ctx      - context variable
    evaluate - a pointer to the user's evaluation function, which must
    have the calling sequence shown in the example.

  Outputs:
    none

  Examples:
    PGAContext *ctx,
    double f(PGAContext *ctx, int p, int pop);
    :
    PGARun(ctx, f);
      
****************************************************************************U*/
void PGARun(PGAContext *ctx,
	    double (*evaluate)(PGAContext *c, int p, int pop))
{
     int monc;

     PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGARun", "Entered", 
		  PGA_VOID, NULL );

     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 );
	
	  PGAEvaluate         ( ctx, PGA_NEWPOP, (*evaluate) );
	  PGAFitness          ( ctx, PGA_NEWPOP );
	  PGAUpdateGeneration ( ctx             );
	  PGAPrintReport      ( ctx, stdout, PGA_OLDPOP );
     }
     PGADebugPrint( ctx, PGADEBUG_EXIT, "PGARun", "Exit", 
                   PGA_VOID, NULL );
     return;
}

/*U****************************************************************************
  PGARunMutateAndCross - Performs crossover and mutation from one population
  to create the next.  Assumes PGASelect has been called.

  Category: Generation

  Inputs:
    ctx - context variable
    oldpop - symbolic constant of old population
    newpop - symbolic constant of new population

  Outputs:
    none

  Example:
    PGAContext *ctx,
    :
    PGARunMutateAndCross(ctx, PGA_OLDPOP, PGA_NEWPOP);

****************************************************************************U*/
void PGARunMutateAndCross (PGAContext *ctx, int oldpop, int newpop)
{
    int i, j, n, m1, m2;
    int popsize, numreplace;
    double pc;

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGARunMutateAndCross", "Entered", 
                   PGA_VOID, NULL );
    popsize = PGAGetPopSize(ctx);
    numreplace = PGAGetNumReplace(ctx);
    /*** first, copy n best strings (sorted by fitness) to new pop ***/
    PGASortPop( ctx, oldpop );
    n = popsize - numreplace;
    for ( i=0; i < n; i++ ) {
        j = PGAGetSortPop( ctx, i );
        PGACopyIndividual ( ctx, j, oldpop, i, newpop );
    }
    pc = PGAGetCrossoverProb(ctx);
    /*** reproduce to create the rest of the new population ***/
    while ( n < popsize) {
        m1 = PGASelectNext( ctx );
	m2 = PGASelectNext( ctx );        
	if ( PGARandomFlip(ctx, pc) ) {
	     PGACrossover ( ctx, m1, m2, oldpop, PGA_TEMP1,
                            PGA_TEMP2, newpop);
             
	      /*** mutate and copy first string to new population ***/
	      PGAMutate ( ctx, PGA_TEMP1, newpop);
	      while (PGADuplicate( ctx, PGA_TEMP1, newpop, newpop, n))
		   PGAChange ( ctx, PGA_TEMP1, newpop );
	      PGACopyIndividual ( ctx, PGA_TEMP1, newpop, n, newpop);
	      n++;

              if ( n < popsize ) {
              /*** mutate and copy second string to new population ***/
  	      PGAMutate ( ctx, PGA_TEMP2, newpop);
	      while ( PGADuplicate( ctx, PGA_TEMP2, newpop, newpop, n))
		   PGAChange ( ctx, PGA_TEMP2, newpop );
	      PGACopyIndividual ( ctx, PGA_TEMP2, newpop, n, newpop);
	      n++;
              }
	 }
	 else {
            PGACopyIndividual ( ctx, m1, oldpop, n, newpop );
            n++;
            if ( n < ctx->ga.PopSize ) {
                PGACopyIndividual ( ctx, m2, oldpop, n, newpop );
                n++;
            }
       }
    }

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGARunMutateAndCross", "Exit", 
                   PGA_VOID, NULL );
    return;
}


/*U****************************************************************************
  PGARunMutateOrCross - Performs crossover or mutation (but not both) from
  one populationto create the next.  Assumes PGASelect has been called.

  Category: Generation

  Inputs:
    ctx - context variable
    oldpop - symbolic constant of old population
    newpop - symbolic constant of new population

  Outputs:
    none

  Example:
    PGAContext *ctx,
    :
    PGARunMutateOrCross(ctx, PGA_OLDPOP, PGA_NEWPOP);

****************************************************************************U*/
void PGARunMutateOrCross ( PGAContext *ctx, int oldpop, int newpop )
{
    int i, j, n, m1, m2;
    int popsize, numreplace;
    double pc;

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGARunMutateOrCross", "Entered", 
                   PGA_VOID, NULL );
    
    popsize = PGAGetPopSize(ctx);
    numreplace = PGAGetNumReplace(ctx);
    /*** first, copy n best strings (sorted by fitness) to new pop ***/
    PGASortPop( ctx, oldpop );
    n = popsize - numreplace;
    for ( i=0; i < n; i++ ) {
        j = PGAGetSortPop( ctx, i );
        PGACopyIndividual ( ctx, j, oldpop, i, newpop );
    }
    pc = PGAGetCrossoverProb(ctx);
    /*** reproduce to create the rest of the new population ***/
    while ( n < popsize ) {
        m1 = PGASelectNext( ctx );
	m2 = PGASelectNext( ctx );        
	if ( PGARandomFlip(ctx, pc) ) {
            PGACrossover ( ctx, m1, m2, oldpop, PGA_TEMP1,
                                        PGA_TEMP2, newpop);
            
            /*** copy first string to new population ***/
            while (PGADuplicate(ctx, PGA_TEMP1, newpop,  newpop, n))
                PGAChange ( ctx, PGA_TEMP1, newpop );
            PGACopyIndividual ( ctx, PGA_TEMP1, newpop, n, newpop);
            n++;

            if ( n < popsize ) {
            /*** copy second string to new population ***/
            if ( n >= popsize )
                break;
            while (PGADuplicate(ctx, PGA_TEMP2, newpop,  newpop, n))
                    PGAChange ( ctx, PGA_TEMP2, newpop );
            PGACopyIndividual ( ctx, PGA_TEMP2, newpop, n, newpop);
            n++;
            }
        }    
        else
	{
	     PGACopyIndividual(ctx, m1, oldpop, PGA_TEMP1, newpop);
	     PGAMutate ( ctx, PGA_TEMP1, newpop );
	     while (PGADuplicate(ctx, PGA_TEMP1, newpop, newpop, n ))
		  PGAChange ( ctx, PGA_TEMP1, newpop );
	     PGACopyIndividual ( ctx, PGA_TEMP1, newpop, n, newpop);
	     n++;
            
	     if ( n < popsize ) {
		  PGACopyIndividual(ctx, m2, oldpop, PGA_TEMP2, newpop);
		  PGAMutate ( ctx, PGA_TEMP2, newpop );
		  while (PGADuplicate(ctx, PGA_TEMP2, newpop, newpop, n ))
		       PGAChange ( ctx, PGA_TEMP2, newpop );
		  PGACopyIndividual ( ctx, PGA_TEMP2, newpop, n, newpop);
		  n++;
	     }
        }
    }        

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGARunMutateOrCross", "Exit", 
                   PGA_VOID, NULL );
}


/*I****************************************************************************
  PGAGetIndividual - translate string index and population symbolic constant
  into pointer to an individual

  Inputs:
     ctx - context variable
     p   - string index
     pop - symbolic constant of the population the string is in

  Outputs:
     Address of the PGAIndividual structure for string p in population pop

  Example:
    PGAIndividual *source;
    PGAContext *ctx;
    int p;
    :
    source = PGAGetIndividual ( ctx, p, PGA_NEWPOP );

****************************************************************************I*/
PGAIndividual *PGAGetIndividual ( PGAContext *ctx, int p, int pop)
{
    PGAIndividual *ind;
    	
    PGADebugPrint( ctx, PGADEBUG_ENTEREDHIGH, "PGAGetIndividual", "Entered", 
                   PGA_VOID, NULL );
    
    switch ( pop ) {
    case PGA_OLDPOP:
	switch ( p ) {
	case PGA_TEMP1:
	    ind = &ctx->ga.oldpop[ctx->ga.PopSize];
	    break;
	case PGA_TEMP2:
	    ind = &ctx->ga.oldpop[ctx->ga.PopSize+1];
	    break;
	default:
	    if ( (p < ctx->ga.PopSize) && (p >= 0) )
		ind = &ctx->ga.oldpop[p];
	    else
		PGAError( ctx, "PGAGetIndividual: Invalid value of p:",
                          PGA_FATAL, PGA_INT, (void *) &p );
	    break;
	};
	break;
    case PGA_NEWPOP:
	switch ( p ) {
	case PGA_TEMP1:
	    ind = &ctx->ga.newpop[ctx->ga.PopSize];
	    break;
	case PGA_TEMP2:
	    ind = &ctx->ga.newpop[ctx->ga.PopSize+1];
	    break;
	default:
	    if ( (p < ctx->ga.PopSize) && (p >= 0) )
		ind = &ctx->ga.newpop[p];
	    else
		PGAError( ctx, "PGAGetIndividual: Invalid value of p:",
			 PGA_FATAL, PGA_INT, (void *) &p );
	    break;
	};
	break;
    default:
	PGAError( ctx, "PGAGetIndividual: Invalid value of pop:",
                  PGA_FATAL, PGA_INT, (void *) &pop );
        break;
    }
    return(ind);
}


/*U****************************************************************************
  PGACopyIndividual - copies string p1 in population pop1 to position p2 in
  population pop2

  Category: Generation

  Inputs:
     ctx  - context variable
     p1   - string to copy
     pop1 - symbolic constant of population containing string p1
     p2   - string to copy p1 to
     pop2 - symbolic constant of population containing string p2

  Outputs:
     None

  Example:
    PGAContext *ctx;
    int i,j;
    :
    PGACopyIndividual(ctx, i, PGA_OLDPOP, j, PGA_NEWPOP);

****************************************************************************I*/
void PGACopyIndividual( PGAContext *ctx, int p1, int pop1, int p2, int pop2)
{
    PGAIndividual *source, *dest;

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGACopyIndividual", "Entered", 
                   PGA_VOID, NULL );

    source = PGAGetIndividual ( ctx, p1, pop1 );
    dest   = PGAGetIndividual ( ctx, p2, pop2 );
    
    dest->evalfunc     = source->evalfunc;
    dest->fitness      = source->fitness;
    dest->evaluptodate = source->evaluptodate;
    
    (*ctx->ops.CopyString)(ctx, p1, pop1, p2, pop2);

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGACopyIndividual", "Exit", 
                   PGA_VOID, NULL );
    return;
}


/*U****************************************************************************
  PGAUpdateGeneration - updates internal data structures for the next
  genetic algorityhm iteration

  Category: Generation

  Inputs:
     ctx  - context variable

  Outputs:
     Reinitializes several internal arrays

  Example:
    PGAContext *ctx;
    :
    PGAUpdateGeneration(ctx);

****************************************************************************I*/
void PGAUpdateGeneration ( PGAContext *ctx )
{
    PGAIndividual *temp;
    int i;
    
    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGAUpdateGeneration", "Entered", 
                   PGA_VOID, NULL );
    PGADebugPrint( ctx, PGADEBUG_PRINTVAR,"PGAUpdateGeneration", "ga.iter = ", 
                   PGA_INT, (void *) &(ctx->ga.iter) );

    ctx->ga.iter++;

    if ((ctx->rep.PrintOptions & PGA_ONLINE) == PGA_ONLINE)
	 PGAUpdateOnline(ctx, PGA_NEWPOP);

    if ((ctx->rep.PrintOptions & PGA_OFFLINE) == PGA_OFFLINE)
	 PGAUpdateOffline(ctx, PGA_NEWPOP);

    if ((ctx->ga.StoppingRule & PGA_STOP_NOCHANGE) == PGA_STOP_NOCHANGE)
    {
	 i = PGAGetBest(ctx, PGA_NEWPOP);
	 if (ctx->rep.Best == PGAGetEvaluate(ctx, i, PGA_NEWPOP))
	      ctx->ga.ItersOfSame++;
	 else
	 {
	      ctx->rep.Best = PGAGetEvaluate(ctx, i, PGA_NEWPOP);
	      ctx->ga.ItersOfSame = 1;
	 }
    }
    
    if ((ctx->ga.StoppingRule & PGA_STOP_TOOSIMILAR) == PGA_STOP_TOOSIMILAR)
	 ctx->ga.PercentSame = PGAComputeSimilarity(ctx, ctx->ga.newpop);
   
    ctx->ga.SelectIndex = 0;
    temp           = ctx->ga.oldpop;
    ctx->ga.oldpop = ctx->ga.newpop;
    ctx->ga.newpop = temp;

    PGADebugPrint( ctx, PGADEBUG_PRINTVAR,"PGAUpdateGeneration", "ga.iter = ", 
                   PGA_INT, (void *) &(ctx->ga.iter) );
    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGAUpdateGeneration", "Exit", 
                   PGA_VOID, NULL );
    return;
}


/*U****************************************************************************
  PGADone - returns boolean to indicate if the termination condition(s)
  has been met

  Category: Generation

  Inputs:
     ctx  - context variable

  Outputs:
     returns PGATRUE if at least one of the termination conditions has been
     met.  Otherwise, returns PGAFALSE

  Example:
    PGAContext *ctx;
    :
    PGADone(ctx);

****************************************************************************U*/
int PGADone( PGAContext *ctx)
{
    int done = PGAFALSE;

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGADone", "Entered", 
                   PGA_VOID, NULL );
    PGADebugPrint( ctx, PGADEBUG_PRINTVAR,"PGADone", "ga.iter    = ", 
                   PGA_INT, (void *) &(ctx->ga.iter) );
    PGADebugPrint( ctx, PGADEBUG_PRINTVAR,"PGADone", "ga.MaxIter = ", 
                   PGA_INT, (void *) &(ctx->ga.MaxIter) );

    if (((ctx->ga.StoppingRule & PGA_STOP_MAXITER) == PGA_STOP_MAXITER) &&
	(ctx->ga.iter > ctx->ga.MaxIter))
	 done = PGATRUE;
    else
	 if (((ctx->ga.StoppingRule & PGA_STOP_NOCHANGE) == PGA_STOP_NOCHANGE)
	     && (ctx->ga.ItersOfSame >= ctx->ga.MaxNoChange))
	      done = PGATRUE;
	 else
	      if (((ctx->ga.StoppingRule & PGA_STOP_TOOSIMILAR) ==
		   PGA_STOP_TOOSIMILAR) && (ctx->ga.PercentSame >=
					    ctx->ga.MaxSimilarity))
		   done = PGATRUE;

    PGADebugPrint( ctx, PGADEBUG_PRINTVAR,"PGADone", "done = ", 
                   PGA_INT, (void *) &done );
    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGADone", "Exit", 
                   PGA_VOID, NULL );
    return(done);
}



/*I****************************************************************************
  PGAGetPopAddress -  returns address of the population referred to by the
  symbolic name

  Inputs:
     ctx   - context variable
     popix - symbolic constant of the population the string is in

  Outputs:
     Address of the population popix

  Example:
    PGAIndividual *popaddress
    PGAContext *ctx;
    :
    popaddress = PGAGetIndividual ( ctx, PGA_NEWPOP );

****************************************************************************I*/
PGAIndividual *PGAPopAddress( PGAContext *ctx, int popix )
{
    PGAIndividual *pop;      /* population pointer */

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGAPopAddress", "Entered", 
                   PGA_VOID, NULL );

    switch (popix) {
    case PGA_OLDPOP:                
        pop = ctx->ga.oldpop;
        break;
    case PGA_NEWPOP:                
        pop = ctx->ga.newpop;
        break;
    default:
        PGAError( ctx,
                 "PGAPopAddress: Invalid value of popix:",
                  PGA_FATAL,
                  PGA_INT,
                  (void *) &popix );
        break;
    }
    
    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGAPopAddress", "Exit", 
                   PGA_VOID, NULL );
    return(pop);
}


/*U****************************************************************************
  PGADestroy - deallocate memory for this instance of PGAPack, if this context
  initialized MPI, finalize MPI as well

  Category: Generation

  Inputs:
     ctx   - context variable

  Outputs:
     None

  Example:
    PGAContext *ctx;
    :
    PGADestroy(ctx);

****************************************************************************U*/
void PGADestroy (PGAContext *ctx)
{
    int i;
  
    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGADestroy", "Entered", 
                   PGA_VOID, NULL );

    for ( i = 0; i < ctx->ga.PopSize + 2; i++ )
        free ( ctx->ga.oldpop[i].chrom );
    free ( ctx->ga.oldpop );
    
    for ( i = 0; i < ctx->ga.PopSize + 2; i++ )
        free ( ctx->ga.newpop[i].chrom );
    free ( ctx->ga.newpop );

    free ( ctx->scratch.intscratch );
    free ( ctx->scratch.dblscratch );
    free ( ctx->ga.selected );
    free ( ctx->ga.sorted );
    if (ctx->ga.datatype == PGA_DATATYPE_REAL)
    {
	 free ( ctx->init.RealMax );
	 free ( ctx->init.RealMin );
    }
    else if (ctx->ga.datatype == PGA_DATATYPE_INTEGER)
    {
	 free ( ctx->init.IntegerMax );
	 free ( ctx->init.IntegerMin );
    }

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGADestroy", "Exit", 
			    PGA_VOID, NULL );
    free ( ctx );

    return;
}


/*U****************************************************************************
  PGACreate - creates a context variable.  The Fortran version of this
  function call contains only the last three arguments

  Category: Generation

  Inputs:
     argc     - address of the count of the number of command line arguments
     argv     - array of command line arguments
     datatype - the data type used for the strings.  Must be one of
                PGA_DATATYPE_BINARY, PGA_DATATYPE_INTEGER, PGA_DATATYPE_REAL,
                or PGA_DATATYPE_USER to specify binary-valued, binary-valued,
                binary-valued, or user-defined, respectively
     len      - the string length (number of genes)
     maxormin - the direction of optimization. Must be one of PGA_MAXIMIZE or
                PGA_MINIMIZE for maximization or minimization, respectively.

  Outputs:
     the address of the context variable

  Example:
     int argc;
     char **argv;
     PGAContext *ctx;
     :
     ctx = PGACreate(&argc, argv, PGA_DATATYPE_BINARY, 100, PGA_MAXIMIZE);

     In Fortran

     integer ctx
     :
     ctx = PGACreate(PGA_DATATYPE_BINARY, 100, PGA_MAXIMIZE)

****************************************************************************U*/
PGAContext *PGACreate ( int *argc, char **argv,
                        int datatype, int len, int maxormin )
{
    int i;
    PGAContext *ctx;

    ctx = (PGAContext *) malloc ( sizeof(PGAContext) );
    if( ctx == NULL )
	 PGAError(ctx, "PGACreate: No room to allocate ctx", PGA_FATAL,
		  PGA_VOID, NULL);

/* parse command line arguments, first initialize debug arguments */

    for ( i=0; i<=PGADEBUG_NUMFLAGS; i++)
        ctx->debug.PGADebugFlags[i] = PGAFALSE;

    PGAReadCmdLine( ctx, argc, argv );

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGACreate", "Entered", 
                   PGA_VOID, NULL );

/* required parameter 1: abstract data type */

    switch (datatype) {
        case PGA_DATATYPE_BINARY:
        case PGA_DATATYPE_INTEGER:
        case PGA_DATATYPE_REAL:
        case PGA_DATATYPE_USER:
          ctx->ga.datatype  = datatype;
	  break;
        default:
	  PGAError( ctx, "PGACreate: Invalid value of datatype:",
                    PGA_FATAL, PGA_INT, (void *) &datatype );
    };

/* required parameter 2: string string length */

    if (len <= 1)
        PGAError( ctx,  "PGACreate: Invalid value of len:",
                  PGA_FATAL, PGA_INT, (void *) &len );
    else
        ctx->ga.StringLen = len;
    

/* required parameter 3: optimization direction */

    switch (maxormin) {
        case PGA_MAXIMIZE:
        case PGA_MINIMIZE:
          ctx->ga.optdir = maxormin;
	  break;
        default:
	  PGAError( ctx, "PGACreate: Invalid value of optdir:",
                    PGA_FATAL, PGA_INT, (void *) &maxormin );
    };
	
    switch (datatype) {
    case PGA_DATATYPE_BINARY:
        ctx->ga.fw = ctx->ga.StringLen/WL;
	ctx->ga.eb = ctx->ga.StringLen%WL;
        if ( ctx->ga.eb == 0 )   
	    ctx->ga.tw = ctx->ga.fw;
        else
            ctx->ga.tw = ctx->ga.fw+1;
	break;
    default:
	ctx->ga.fw = PGA_UNINITIALIZED_INT;
	ctx->ga.eb = PGA_UNINITIALIZED_INT;
	ctx->ga.tw = PGA_UNINITIALIZED_INT;
	break;
    }

    ctx->ga.PopSize            = PGA_UNINITIALIZED_INT;
    ctx->ga.StoppingRule       = 0;
    ctx->ga.MaxIter            = PGA_UNINITIALIZED_INT;
    ctx->ga.MaxNoChange        = PGA_UNINITIALIZED_INT;
    ctx->ga.MaxSimilarity      = PGA_UNINITIALIZED_INT;
    ctx->ga.NumReplace         = PGA_UNINITIALIZED_INT;
    ctx->ga.CrossoverType      = PGA_UNINITIALIZED_INT;
    ctx->ga.SelectType         = PGA_UNINITIALIZED_INT;
    ctx->ga.FitnessType        = PGA_UNINITIALIZED_INT;
    ctx->ga.FitnessMinType     = PGA_UNINITIALIZED_INT;
    ctx->ga.MutationType       = PGA_UNINITIALIZED_INT;
    ctx->ga.MutateOnlyNoCross  = PGA_UNINITIALIZED_INT;
    ctx->ga.MutateRealVal     = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.MutateIntegerVal    = PGA_UNINITIALIZED_INT;
    ctx->ga.NoDuplicates       = PGA_UNINITIALIZED_INT;
    ctx->ga.MutationProb       = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.CrossoverProb      = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.UniformCrossProb   = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.PTournamentProb    = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.FitnessRankMax     = PGA_UNINITIALIZED_DOUBLE;
    ctx->ga.PopReplace         = PGA_UNINITIALIZED_INT;
    ctx->ga.iter               = 0;
    ctx->ga.ItersOfSame        = 0;
    ctx->ga.PercentSame        = 0;
    ctx->ga.selected           = NULL;
    ctx->ga.SelectIndex        = 0;

/* ops */

    ctx->ops.CreateString  = NULL;
    ctx->ops.Mutation          = NULL;
    ctx->ops.Crossover         = NULL;
    ctx->ops.WriteString   = NULL;
    ctx->ops.CopyString    = NULL;
    ctx->ops.Duplicate         = NULL;
    ctx->ops.Randomize         = NULL;

/* rep */
    ctx->rep.PrintFreq         = PGA_UNINITIALIZED_INT;
    ctx->rep.PrintOptions      = 0;
    ctx->rep.Online            = 0;
    ctx->rep.Offline           = 0;
    ctx->rep.Best              = PGA_UNINITIALIZED_DOUBLE;

/* sys */
    if ( ctx->sys.fortran != PGATRUE )     /* if not set to PGATRUE in       */
                                           /* pgacreate_ user program is in C*/
        ctx->sys.fortran = PGAFALSE;
    ctx->sys.SetUpCalled       = PGAFALSE;
    ctx->sys.PGAMaxInt         = INT_MAX;
    ctx->sys.PGAMinInt         = INT_MIN;
    ctx->sys.PGAMaxDouble      = DBL_MAX;
    ctx->sys.PGAMinDouble      = DBL_MIN;
/* debug */
    /* set above before parsing command line arguments */
/* init */
    ctx->init.RandomInit       = PGA_UNINITIALIZED_INT;
    ctx->init.BinaryProbability   = PGA_UNINITIALIZED_DOUBLE;
    ctx->init.IntegerType       = PGA_UNINITIALIZED_INT;
    ctx->init.RandomSeed       = PGA_UNINITIALIZED_INT;

    switch (datatype)
    {
    case PGA_DATATYPE_INTEGER:
	 ctx->init.IntegerMax = (int *) malloc(len * sizeof(PGAInteger));
	 if (!ctx->init.IntegerMax)
	      PGAError(ctx, "PGACreate: No room to allocate:", PGA_FATAL,
		       PGA_CHAR, (void *) "ctx->init.IntegerMax");
	 ctx->init.IntegerMin = (int *) malloc(len * sizeof(PGAInteger));
	 if (!ctx->init.IntegerMin)
	      PGAError(ctx, "PGACreate: No room to allocate:", PGA_FATAL,
		       PGA_CHAR, (void *) "ctx->init.IntegerMin");
	 ctx->init.RealMax = NULL;
	 ctx->init.RealMin = NULL;
	 for (i = 0; i < len; i++)
	 {
	      ctx->init.IntegerMax[i] = PGA_UNINITIALIZED_INT;
	      ctx->init.IntegerMax[i] = PGA_UNINITIALIZED_INT;
	 }
	 break;
    case PGA_DATATYPE_REAL:
	 ctx->init.RealMax = (PGAReal *) malloc(len * sizeof(PGAReal));
	 if (!ctx->init.RealMax)
	      PGAError(ctx, "PGACreate: No room to allocate:", PGA_FATAL,
		       PGA_CHAR, (void *) "ctx->init.RealMax");
	 ctx->init.RealMin = (PGAReal *) malloc(len * sizeof(PGAReal));
	 if (!ctx->init.RealMin)
	      PGAError(ctx, "PGACreate: No room to allocate:", PGA_FATAL,
		       PGA_CHAR, (void *) "ctx->init.RealMin");
	 ctx->init.IntegerMax = NULL;
	 ctx->init.IntegerMin = NULL;
	 for (i = 0; i < len; i++)
	 {
	      ctx->init.RealMax[i] = PGA_UNINITIALIZED_DOUBLE;
	      ctx->init.RealMax[i] = PGA_UNINITIALIZED_DOUBLE;
	 }
	 break;
    default:
	 ctx->init.RealMax = NULL;
	 ctx->init.RealMin = NULL;
	 ctx->init.IntegerMax = NULL;
	 ctx->init.IntegerMin = NULL;
	 break;
    }
	 

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGACreate", "Exit", 
                   PGA_VOID, NULL );

    return(ctx);
}


/*U****************************************************************************
  PGASetUp - set all uninitialized variables to default values and initialize
  some internal arrays

  Category: Generation

  Inputs:
     ctx - context variable

  Outputs:
     None

  Example:
     PGAContext *ctx;
     :
     PGASetUp(ctx);

****************************************************************************U*/
void PGASetUp ( PGAContext *ctx )
{
    int err=0, i;

    PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGASetUp", "Entered", 
                   PGA_VOID, NULL );

    ctx->sys.SetUpCalled = PGATRUE;

    if ( ctx->ga.datatype == PGA_DATATYPE_BINARY   &&
	ctx->ga.tw       == PGA_UNINITIALIZED_INT )
         PGAError( ctx,
                 "PGASetUp: Binary: Total Words (ctx->ga.tw) == UNINITIALIZED?",
                  PGA_FATAL, PGA_INT, (void *) &ctx->ga.tw );
    
    if ( ctx->ga.datatype == PGA_DATATYPE_BINARY  &&
	 ctx->ga.fw       == PGA_UNINITIALIZED_INT )
         PGAError( ctx,
                 "PGASetUp: Binary: Full Words (ctx->ga.fw) == UNINITIALIZED?",
                  PGA_FATAL, PGA_INT,  (void *) &ctx->ga.fw );
    
    if ( ctx->ga.datatype == PGA_DATATYPE_BINARY  &&
	 ctx->ga.tw       == PGA_UNINITIALIZED_INT )
	 PGAError( ctx,
                 "PGASetUp: Binary: Empty Bits (ctx->ga.eb) == UNINITIALIZED?",
                  PGA_FATAL, PGA_INT, (void *) &ctx->ga.eb );
    
    if ( ctx->ga.PopSize == PGA_UNINITIALIZED_INT)
         ctx->ga.PopSize  = 100;

    if ( ctx->ga.StoppingRule == 0)
         ctx->ga.StoppingRule  = PGA_STOP_MAXITER;

    if ( ctx->ga.MaxIter == PGA_UNINITIALIZED_INT)
         ctx->ga.MaxIter  = 1000;

    if ( ctx->ga.MaxNoChange == PGA_UNINITIALIZED_INT)
	 ctx->ga.MaxNoChange = 50;
    
    if ( ctx->ga.MaxSimilarity == PGA_UNINITIALIZED_INT)
	 ctx->ga.MaxSimilarity = 95;
    
    if ( ctx->ga.NumReplace == PGA_UNINITIALIZED_INT)
         ctx->ga.NumReplace  = (int) ceil(ctx->ga.PopSize * 0.1);

    if ( ctx->ga.CrossoverType == PGA_UNINITIALIZED_INT)
         ctx->ga.CrossoverType  = PGA_CROSSOVER_TWOPT;

    if (ctx->ga.CrossoverType == PGA_CROSSOVER_TWOPT &&
	ctx->ga.StringLen == 2)
	 PGAError(ctx, "PGASetUp: Invalid Crossover type for string of length"
		  "2", PGA_FATAL, PGA_INT, (void *) &ctx->ga.CrossoverType);

    if ( ctx->ga.SelectType == PGA_UNINITIALIZED_INT)
         ctx->ga.SelectType  = PGA_SELECT_TOURNAMENT;

    if ( ctx->ga.FitnessType == PGA_UNINITIALIZED_INT)
         ctx->ga.FitnessType  = PGA_FITNESS_RAW;

    if ( ctx->ga.FitnessMinType == PGA_UNINITIALIZED_INT)
         ctx->ga.FitnessMinType  = PGA_FITNESSMIN_CMAX;

    if (ctx->ga.MutationProb != PGA_UNINITIALIZED_DOUBLE)
	 ctx->ga.MutationType = PGA_MUTATION_FIXED;

    if ( ctx->ga.MutationType == PGA_UNINITIALIZED_INT)
         ctx->ga.MutationType  = PGA_MUTATION_STRINGLEN;

    if ( ctx->ga.MutationType == PGA_MUTATION_STRINGLEN )
         ctx->ga.MutationProb = 1. / ctx->ga.StringLen;
    else
        if ( ctx->ga.MutationProb == PGA_UNINITIALIZED_DOUBLE)
            ctx->ga.MutationProb = 0.001;
/*    fprintf(fp, "ctx->ga.MutationProb = %f\n",ctx->ga.MutationProb); */

    if ( ctx->ga.MutateOnlyNoCross == PGA_UNINITIALIZED_INT)
         ctx->ga.MutateOnlyNoCross  = PGATRUE;

    if ( ctx->ga.MutateRealVal == PGA_UNINITIALIZED_DOUBLE)
         ctx->ga.MutateRealVal  = 0.1;

    if ( ctx->ga.MutateIntegerVal == PGA_UNINITIALIZED_INT)
         ctx->ga.MutateIntegerVal  = 1;

    if ( ctx->ga.NoDuplicates == PGA_UNINITIALIZED_INT)
         ctx->ga.NoDuplicates  = PGAFALSE;

    if ( ctx->ga.NoDuplicates && ((ctx->ga.StoppingRule & PGA_STOP_TOOSIMILAR)
				  == PGA_STOP_TOOSIMILAR))
	 PGAError(ctx, "PGASetUp: No Duplicates inconsistent with Stopping "
		  "Rule:", PGA_FATAL, PGA_INT, (void *) &ctx->ga.StoppingRule);

    if ( ctx->ga.CrossoverProb == PGA_UNINITIALIZED_DOUBLE)
         ctx->ga.CrossoverProb  = 0.85;

    if ( ctx->ga.UniformCrossProb == PGA_UNINITIALIZED_DOUBLE)
         ctx->ga.UniformCrossProb  = 0.6;

    if ( ctx->ga.PTournamentProb == PGA_UNINITIALIZED_DOUBLE)
	 ctx->ga.PTournamentProb = 0.6;
    
    if ( ctx->ga.FitnessRankMax == PGA_UNINITIALIZED_DOUBLE)
         ctx->ga.FitnessRankMax  = 1.2;

    if ( ctx->ga.PopReplace == PGA_UNINITIALIZED_INT)
         ctx->ga.PopReplace  = PGA_POPREPL_BEST;

/* ops */

    switch (ctx->ga.datatype) {
    case PGA_DATATYPE_BINARY:
	if (ctx->ops.CreateString  == NULL)
	    ctx->ops.CreateString  = PGABinaryCreateString;
	if (ctx->ops.Mutation          == NULL)
	    ctx->ops.Mutation          = PGABinaryMutation;
	switch (ctx->ga.CrossoverType) {
        case PGA_CROSSOVER_ONEPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGABinaryOneptCrossover;
	    break;
        case PGA_CROSSOVER_TWOPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGABinaryTwoptCrossover;
	    break;
        case PGA_CROSSOVER_UNIFORM:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGABinaryUniformCrossover;
	    break;
	}
	if (ctx->ops.WriteString   == NULL)
	    ctx->ops.WriteString   = PGABinaryWriteString;
	if (ctx->ops.CopyString    == NULL)
	    ctx->ops.CopyString    = PGABinaryCopyString;
	if (ctx->ops.Duplicate         == NULL)
	    ctx->ops.Duplicate         = PGABinaryDuplicate;
	if (ctx->ops.Randomize         == NULL)
	     ctx->ops.Randomize        = PGABinaryRandomize; 
	break;
    case PGA_DATATYPE_INTEGER:
	if (ctx->ops.CreateString  == NULL)
	    ctx->ops.CreateString  = PGAIntegerCreateString;
	if (ctx->ops.Mutation          == NULL)
	    ctx->ops.Mutation          = PGAIntegerMutation;
	switch (ctx->ga.CrossoverType) {
        case PGA_CROSSOVER_ONEPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGAIntegerOneptCrossover;
	    break;
        case PGA_CROSSOVER_TWOPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGAIntegerTwoptCrossover;
	    break;
        case PGA_CROSSOVER_UNIFORM:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGAIntegerUniformCrossover;
	    break;
	}
	if (ctx->ops.WriteString   == NULL)
	    ctx->ops.WriteString   = PGAIntegerWriteString;
	if (ctx->ops.CopyString    == NULL)
	    ctx->ops.CopyString    = PGAIntegerCopyString;
	if (ctx->ops.Duplicate         == NULL)
	    ctx->ops.Duplicate         = PGAIntegerDuplicate;
	if (ctx->ops.Randomize         == NULL)
	     ctx->ops.Randomize        = PGAIntegerRandomize;
	break;
    case PGA_DATATYPE_REAL:
	if (ctx->ops.CreateString  == NULL)
	    ctx->ops.CreateString  = PGARealCreateString;
	if (ctx->ops.Mutation          == NULL)
	    ctx->ops.Mutation          = PGARealMutation;
	switch (ctx->ga.CrossoverType) {
        case PGA_CROSSOVER_ONEPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGARealOneptCrossover;
	    break;
        case PGA_CROSSOVER_TWOPT:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGARealTwoptCrossover;
	    break;
        case PGA_CROSSOVER_UNIFORM:
	    if (ctx->ops.Crossover     == NULL)
		ctx->ops.Crossover     = PGARealUniformCrossover;
	    break;
	}
	if (ctx->ops.WriteString   == NULL)
	    ctx->ops.WriteString   = PGARealWriteString;
	if (ctx->ops.CopyString    == NULL)
	    ctx->ops.CopyString    = PGARealCopyString;
	if (ctx->ops.Duplicate         == NULL)
	    ctx->ops.Duplicate         = PGARealDuplicate;
	if (ctx->ops.Randomize         == NULL)
	     ctx->ops.Randomize        = PGARealRandomize;
	break;
    case PGA_DATATYPE_USER:
	if (ctx->ops.CreateString == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs CreateString function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	if (ctx->ops.Mutation == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs Mutation function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	if (ctx->ops.Crossover == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs Crossover function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	if (ctx->ops.WriteString == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs WriteString function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	if (ctx->ops.CopyString == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs CopyString function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	if (ctx->ops.Duplicate == NULL)
	    PGAError( ctx,
		    "PGASetUp: User datatype needs Duplicate function:",
		     PGA_WARNING, PGA_INT, (void *) &err );
	break;
    }
    
/* rep */

    if ( ctx->rep.PrintFreq == PGA_UNINITIALIZED_INT)
         ctx->rep.PrintFreq  = 10;
    
/* sys */
    /* no more sets necessary here. */

    /* debug */

/* init */

    if ( ctx->init.RandomInit == PGA_UNINITIALIZED_INT)
         ctx->init.RandomInit  = PGATRUE;

    if ( ctx->init.BinaryProbability == PGA_UNINITIALIZED_DOUBLE)
         ctx->init.BinaryProbability  = 0.5;
  
    if ( ctx->init.IntegerType == PGA_UNINITIALIZED_INT)
         ctx->init.IntegerType  = PGA_PERMUTE;

    switch (ctx->ga.datatype)
    {
    case PGA_DATATYPE_INTEGER:
	 for (i = 0; i < ctx->ga.StringLen; i++)
	 {
	      if (ctx->init.IntegerMin[i] == PGA_UNINITIALIZED_INT)
		   ctx->init.IntegerMin[i] = 0;
	      if (ctx->init.IntegerMax[i] == PGA_UNINITIALIZED_INT)
		   ctx->init.IntegerMax[i] = ctx->ga.StringLen - 1;
	 }
	 break;
    case PGA_DATATYPE_REAL:
	 for (i = 0; i < ctx->ga.StringLen; i++)
	 {
	      if (ctx->init.RealMin[i] == PGA_UNINITIALIZED_DOUBLE)
		   ctx->init.RealMin[i] = 0.;
	      if (ctx->init.RealMax[i] == PGA_UNINITIALIZED_DOUBLE)
		   ctx->init.RealMax[i] = 1.;
	 }
	 break;
    }
	 
    /* If a seed was not specified, get one from a time of day call */
    if ( ctx->init.RandomSeed == PGA_UNINITIALIZED_INT)
	 ctx->init.RandomSeed = (int)time(NULL);

    /* seed random number generator with this process' unique seed */
    PGARandom01( ctx, ctx->init.RandomSeed );   

    ctx->ga.selected        = (int *)
                              malloc( sizeof(int) * ctx->ga.PopSize );
    if (ctx->ga.selected == NULL)
	 PGAError(ctx, "PGASetUp: No room to allocate ctx->ga.selected",
		  PGA_FATAL, PGA_VOID, NULL);
    
    ctx->ga.sorted          = (int *)
                              malloc( sizeof(int) * ctx->ga.PopSize );
    
    if (ctx->ga.sorted == NULL)
	 PGAError(ctx, "PGASetUp: No room to allocate ctx->ga.sorted",
		  PGA_FATAL, PGA_VOID, NULL);
    
    ctx->scratch.intscratch = (int *)
                              malloc( sizeof(int) * ctx->ga.PopSize );
    
    if (ctx->scratch.intscratch == NULL)
	 PGAError(ctx, "PGASetUp: No room to allocate ctx->scratch.intscratch",
		  PGA_FATAL, PGA_VOID, NULL);
    
    ctx->scratch.dblscratch = (double *)
                              malloc(sizeof(double) * ctx->ga.PopSize);

    if (ctx->scratch.dblscratch == NULL)
	 PGAError(ctx, "PGASetUp: No room to allocate ctx->scratch.dblscratch",
		  PGA_FATAL, PGA_VOID, NULL);
    
    PGACreatePop ( ctx , PGA_OLDPOP );
    PGACreatePop ( ctx , PGA_NEWPOP );

    PGADebugPrint( ctx, PGADEBUG_EXIT, "PGASetUp", "Exit", 
                   PGA_VOID, NULL );
    return;
}


/*I****************************************************************************
  PGACreatePop - allocates a population of individuals and calls
  PGACreateIndividual to set up each one

  Inputs:
     ctx - context variable
     pop - symbolic constant of the population to create

  Outputs:
     None

  Example:
     PGAContext *ctx;
     :
     PGACreatePop(ctx,PGA_NEWPOP);

****************************************************************************I*/
void PGACreatePop (PGAContext *ctx, int pop)
{
     int p, flag;

     PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGACreatePop", "Entered", 
                   PGA_VOID, NULL );

     switch (pop)
     {
     case PGA_OLDPOP:
	  ctx->ga.oldpop = (PGAIndividual *)malloc(sizeof(PGAIndividual) *
						   (ctx->ga.PopSize + 2));
	  if (ctx->ga.oldpop == NULL)
	       PGAError(ctx, "PGACreatePop: No room to allocate "
			"ctx->ga.oldpop", PGA_FATAL, PGA_VOID, NULL);
	  flag = ctx->init.RandomInit;
	  break;
     case PGA_NEWPOP:
	  ctx->ga.newpop = (PGAIndividual *)malloc(sizeof(PGAIndividual) *
						   (ctx->ga.PopSize + 2));
	  if (ctx->ga.newpop == NULL)
	       PGAError(ctx, "PGACreatePop: No room to allocate "
			"ctx->ga.newpop", PGA_FATAL, PGA_VOID, NULL);
	  flag = PGAFALSE;
	  break;
     default:
	  PGAError(ctx, "PGACreatePop: Invalid value of pop:", PGA_FATAL,
		   PGA_INT, (void *) &pop );
	  break;
     };
     for (p = 0; p < ctx->ga.PopSize; p++)
	  PGACreateIndividual (ctx, p, pop, flag);
     PGACreateIndividual (ctx, PGA_TEMP1, pop, PGAFALSE);
     PGACreateIndividual (ctx, PGA_TEMP2, pop, PGAFALSE);

     PGADebugPrint( ctx, PGADEBUG_EXIT, "PGACreatePop", "Exit", 
                   PGA_VOID, NULL );
     return;
}


/*I****************************************************************************
  PGACreateIndividual - initialize to zero various data structures of an
  individual and call the appropriate function to create and initialize the
  string for the specific data type

  Inputs:
     ctx      - context variable
     p        - string index
     pop      - symbolic constant of the population string p is in
     initflag - if the value is PGATRUE, the string is randomly initialized.
                Otherwise it is set to zero.

  Outputs:
     None

  Example:
     PGAContext *ctx;
     PGAIndividual *s;
     :
     PGACreateIndividual(ctx,s,PGATRUE);

****************************************************************************I*/
void PGACreateIndividual (PGAContext *ctx, int p, int pop, int initflag)
{
     PGAIndividual *ind = PGAGetIndividual(ctx, p, pop);

     PGADebugPrint( ctx, PGADEBUG_ENTERED, "PGACreateIndividual",
                   "Entered", PGA_VOID, NULL );
     
     ind->evalfunc     = 0.0;
     ind->fitness      = 0.0;
     ind->evaluptodate = PGAFALSE;
     (*ctx->ops.CreateString)(ctx, p, pop, initflag);

     PGADebugPrint( ctx, PGADEBUG_EXIT, "PGACreateIndividual",
                   "Exit", PGA_VOID, NULL );
     return;
}

/*I****************************************************************************
  PGAUpdateOnline - Updates the online value based on the results in
  the new generation

  Inputs:
     ctx - context variable
     pop - symbolic constant of the population whose statistics to use

  Outputs:
     Updates an internal field in the context variable

  Example:
     PGAContext *ctx;
     :
     PGAUpdateOnline(ctx,PGA_NEWPOP);

**************************************************************************I*/
void PGAUpdateOnline(PGAContext *ctx, int pop)
{
     double ThisGensTotal = 0;
     int p;

     PGADebugPrint(ctx, PGADEBUG_ENTERED, "PGAUpdateOnline", "Entered",
		   PGA_VOID, NULL);

     for (p = 0; p < ctx->ga.PopSize; p++)
	  if (!PGAGetEvalUpToDate(ctx, p, pop))
	       PGAError(ctx, "PGAUpdateOnline: Evaluate function not up to "
			"date:", PGA_FATAL, PGA_INT, (void *) &p);

     for (p = 0; p < ctx->ga.PopSize; p++)
	  ThisGensTotal += PGAGetEvaluate(ctx, p, pop);

     PGADebugPrint(ctx, PGADEBUG_PRINTVAR, "PGAUpdateOnline",
		   "ThisGensTotal = ", PGA_DOUBLE, (void *) &ThisGensTotal);
     
     ctx->rep.Online = (ctx->rep.Online * ctx->ga.PopSize * (ctx->ga.iter - 1)
			+ ThisGensTotal) / ctx->ga.iter / ctx->ga.PopSize;

     PGADebugPrint( ctx, PGADEBUG_EXIT, "PGAUpdateOnline", "Exit", 
                   PGA_VOID, NULL );
     
}

/*I****************************************************************************
  PGAUpdateOffline - Updates the offline value based on the results in
  the new generation

  Inputs:
     ctx - context variable
     pop - symbolic constant of the population whose statistics to use

  Outputs:
     Updates an internal field in the context variable

  Example:
     PGAContext *ctx;
     :
     PGAUpdateOffline(ctx,PGA_NEWPOP);

**************************************************************************I*/
void PGAUpdateOffline(PGAContext *ctx, int pop)
{
     int p;

     PGADebugPrint(ctx, PGADEBUG_ENTERED, "PGAUpdateOffline", "Entered",
		   PGA_VOID, NULL);
          
     for (p = 0; p < ctx->ga.PopSize; p++)
	  if (!PGAGetEvalUpToDate(ctx, p, pop))
	       PGAError(ctx, "PGAUpdateOffline: Evaluate function not up to "
			"date:", PGA_FATAL, PGA_INT, (void *) &p);

     p = PGAGetBest(ctx, pop);
          
     ctx->rep.Offline = ((ctx->ga.iter - 1) * ctx->rep.Offline + 
			 PGAGetEvaluate(ctx, p, pop)) / ctx->ga.iter;
}

/*I****************************************************************************
  PGAComputeSimilarity - computes the percentage of the population that have
  the same evaluation function

  Inputs:
     ctx - context variable
     pop - symbolic constant of the population whose statistics to use

  Outputs:
     returns a count of the number of  population members that have the same
     evaluation function value

  Example:
     PGAContext *ctx;
     :
     PGAComputeSimilarity(ctx,PGA_NEWPOP);

**************************************************************************I*/
int PGAComputeSimilarity(PGAContext *ctx, PGAIndividual *pop)
{
     int max = 0, curr = 1, i;
     double prev;

     for(i=0; i < ctx->ga.PopSize; i++)
     {
	  ctx->scratch.dblscratch[i] = (pop + i)->evalfunc;
	  ctx->scratch.intscratch[i] = i;
     }

     PGADblHeapSort(ctx, ctx->scratch.dblscratch, ctx->scratch.intscratch,
		    ctx->ga.PopSize);
     
     prev = ctx->scratch.dblscratch[0];
     
     for(i = 1; i < ctx->ga.PopSize; i++)
     {
	  if (ctx->scratch.dblscratch[i] == prev)
	       curr++;
	  else
	  {
	       if (curr > max)
		    max = curr;
	       curr = 1;
	  }
     }

     return 100 * max / ctx->ga.PopSize;
}
