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

gp_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].gp_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);
    }
    
    select_context_func_ptr select_con = get_select_context("best");
    struct _sel_context* context = select_con(SELECT_INIT, NULL, oldpop, "best");
    char* elitism_str = get_parameter("elitism");
    
    if (elitism_str == NULL)
        elitism_str = "0";
    
    int elitism = atoi(elitism_str);
    if (elitism < 0)
        error(E_FATAL_ERROR, "elitism must be >= 0");
    
    for (i = 0; i < elitism; i++)
    {
        /* select an individual... */
        j = context->select_method(context);
        
        /* ...and reproduce it into the new population. */
        duplicate_individual((newpop->ind) + newpop->next, (oldpop->ind) + j);
        newpop->ind[newpop->next].flags = FLAG_NONE;
        ++newpop->next;
        printf("\tEliting a new pop!\n");
    }
    
    typedef struct
    {
        int next;
        int *list;
    } bestworst_data;
    
    FREE(((bestworst_data*) context->data)->list);
    FREE(context->data);
    FREE(context);
    
    /* 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].gp_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;
    gp_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].gp_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;
}