/*  lil-gp Genetic Programming System, version 1.0, 11 July 1995
 *  Copyright (C) 1995  Michigan State University
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of version 2 of the GNU General Public License as
 *  published by the Free Software Foundation.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *  
 *  Douglas Zongker       (zongker@isl.cps.msu.edu)
 *  Dr. Bill Punch        (punch@isl.cps.msu.edu)
 *
 *  Computer Science Department
 *  A-714 Wells Hall
 *  Michigan State University
 *  East Lansing, Michigan  48824
 *  USA
 *  
 */

#include <lilgp.h>

/* the table of operator names and initialization functions.  extend this
 * table whenever you add a new operator.  {NULL,NULL} marks the end of
 * the table.
 */

operator operator_table[] =
{ { "crossover",      operator_crossover_init },
  { "reproduction",   operator_reproduce_init },
  { "mutation",       operator_mutate_init },
  { NULL, NULL } };

/* change_population()
 *
 * breed the new population.
 */

population *change_population ( population *oldpop, breedphase *bp )
{
     population *newpop;
     int i, j;
     int numphases;
     double totalrate = 0.0;
     double r, r2;
     int prob_oper = atoi ( get_parameter ( "probabilistic_operators" ) );

     /* allocate the new population. */
     newpop = allocate_population ( oldpop->size );

     /* the first element of the breedphase table is a dummy -- its
	operator field stores the number of phases. */
     numphases = bp[0].operator;

     /* call the start method for each phase. */
     for ( i = 1; i <= numphases; ++i )
     {
          totalrate += bp[i].rate;
          if ( bp[i].operator_start )
               bp[i].operator_start ( oldpop, bp[i].data );
     }

     /* now fill the new population. */
     while ( newpop->next < newpop->size )
     {

	  /** select an operator, either stochastically or not depending on
	    the probabilistic_operators parameter. **/
          if ( prob_oper )
               r = totalrate * random_double ( &globrand );
          else
               r = totalrate * ((double)newpop->next/(double)newpop->size);

          r2 = bp[1].rate;
          for ( i = 1; r2 < r; )
               r2 += bp[++i].rate;
#ifdef DEBUG
          fprintf ( stderr, "picked %10.3lf; operator %d\n", r, i );
#endif

	  /* call the phase's method to do the operation. */
          if ( bp[i].operator_operate )
               bp[i].operator_operate ( oldpop, newpop, bp[i].data );
     }

     /* call each phase's method to do cleanup. */
     for ( i = 1; i <= numphases; ++i )
     {
          if ( bp[i].operator_end )
               bp[i].operator_end ( bp[i].data );
     }

     /* mark all the ERCs referenced in the new population. */
     for ( i = 0; i < newpop->size; ++i )
          for ( j = 0; j < tree_count; ++j )
               reference_ephem_constants ( newpop->ind[i].tr[j].data, 1 );

     /* free the old population. */
     free_population ( oldpop );

     return ( newpop );
     
}

/* free_breeding()
 *
 * this frees the breedphase table for each subpopulation.
 */

void free_breeding ( multipop *mpop )
{
     int i;

     for ( i = 0; i < mpop->size; ++i )
     {
          free_one_breeding ( mpop->bpt[i] );
          FREE ( mpop->bpt[i] );
     }
     FREE ( mpop->bpt );
     mpop->bpt = NULL;
}

/* free_one_breeding()
 *
 * this frees the breedphase table for a single subpopulation.
 */

void free_one_breeding ( breedphase *bp )
{
     int i;

     for ( i = 1; i <= bp[0].operator; ++i )
     {
          if ( bp[i].operator_free )
               bp[i].operator_free ( bp[i].data );
     }
}

/* initialize_breeding()
 *
 * this builds the breedphase table for each subpopulation.
 */

void initialize_breeding ( multipop *mpop )
{
     char pnamebuf[100];
     int i;

     mpop->bpt = (breedphase **)MALLOC ( mpop->size * sizeof ( breedphase * ) );
     
     for ( i = 0; i < mpop->size; ++i )
     {
          sprintf ( pnamebuf, "subpop[%d].", i+1 );
          mpop->bpt[i] = initialize_one_breeding ( pnamebuf );
     }

}

/* initialize_one_breeding()
 *
 * this builds the breedphase table for a single subpopulation. */

breedphase * initialize_one_breeding ( char *prefix )
{
     char pnamebuf[100];
     char *param, *param2;
     int i, j;
     double rate;
     int errors = 0;
     breedphase *bp;
     operator *op;
     char *name, *namep;

     /* get the number of phases. */
     param = get_breed_parameter ( prefix, "breed_phases" );
     if ( param == NULL )
          error ( E_FATAL_ERROR, "no value specified for \"%sbreed_phases\".",
                 prefix );
     j = atoi ( param );
     if ( j <= 0 )
          error ( E_FATAL_ERROR,
                 "\"%sbreed_phases\" must be greater than zero.", prefix );

     /* the first record of the table is a dummy -- its operator field
	contains the number of phases. */
     bp = (breedphase *)MALLOC ( (j+1) * sizeof ( breedphase ) );
     bp[0].operator = j;
     bp[0].rate = 0.0;
     bp[0].operator_start = NULL;
     bp[0].operator_end = NULL;
     bp[0].operator_free = NULL;
     bp[0].operator_operate = NULL;

     /* for each phase... */
     for ( i = 0; i < j; ++i )
     {
          bp[i+1].operator_start = NULL;
          bp[i+1].operator_end = NULL;
          bp[i+1].operator_free = NULL;
          bp[i+1].operator_operate = NULL;

	  /* get the operator string (name and options) */
          param = get_breed_parameter ( prefix, "breed[%d].operator", i+1 );
          if ( param == NULL )
          {
               ++errors;
               error ( E_ERROR,
                      "no value specifed for \"%sbreed[%d].operator\".",
                      prefix, i+1 );
               continue;
          }

	  /* isolate the name portion of the string. */
          name = (char *)MALLOC ( ( strlen ( param ) + 1 ) * sizeof ( char ) );
          namep = name;
          for ( param2 = param, namep = name;
               *param2 && *param2 != ',' && *param2 != '\n';
               ++param2 )
               if ( !isspace(*param2) )
                    *(namep++) = *param2;
          *namep = 0;
          if ( ! *param2 )
               --param2;

          /* look up the name in the table of operators. */
          op = operator_table;
          while ( op->name )
          {
               if ( strcmp ( op->name, name ) == 0 )
                    break;
               ++op;
          }

          FREE ( name );

          if ( op->name )
	       /* call the operator's initialization function to fill in the fields
		  of the table for this phase. */
               errors += op->func ( param2+1, bp+i+1 );
          else
	       /* the specified operator is not in the table. */
               error ( E_FATAL_ERROR,
                      "%s: \"%s\" is not a known operator.", pnamebuf, param );

	  /* get the rate for this phase. */
          param = get_breed_parameter ( prefix, "breed[%d].rate", i+1 );
          if ( param == NULL )
          {
               ++errors;
               error ( E_ERROR,
                      "no value specified for \"%sbreed[%d].rate\".",
                      prefix, i+1 );
          }
          else
          {
               rate = strtod ( param, NULL );
               if ( rate < 0.0 )
               {
                    ++errors;
                    error ( E_FATAL_ERROR,
                           "\"%sbreed[%d].rate\" must be nonnegative.",
                           prefix, i+1 );
               }
               else if ( rate == 0.0 )
               {
                    error ( E_WARNING,
                           "\"%sbreed[%d].rate\" is zero; is this correct?",
                           prefix, i+1 );
               }
               bp[i+1].rate = rate;
          }
     }

     /* if any errors occurred, stop now. */
     if ( errors )
          error ( E_FATAL_ERROR, "Errors have occurred, aborting." );
     
     return bp;
}

/* rebuild_breeding()
 *
 * rebuilds the breeding table from the parameter database.  called from
 * user code when the breeding parameters change mid-run.
 */

void rebuild_breeding ( multipop *mpop )
{
     free_breeding ( mpop );
     initialize_breeding ( mpop );
}

/* get_breed_parameter()
 *
 * format and following arguments are passed to sprintf to form a string.
 * looks for a parameter called "<prefix><string>", and returns its value.
 * if it does not exist, returns the value of a parameter called "<string>".
 */

char *get_breed_parameter ( char *prefix, char *format, ... )
{
     char *param;
     static char pnamebuf[200];
     int len = strlen(prefix);
     va_list ap;

     strcpy ( pnamebuf, prefix );
     va_start ( ap, format );
     vsprintf ( pnamebuf+len, format, ap );
     va_end ( ap );
     
     param = get_parameter ( pnamebuf );
     if ( param == NULL )
          return get_parameter ( pnamebuf+len );
     else
          return param;
}
