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

popstats* run_stats;
saved_ind* saved_head, * saved_tail;

#if !defined(POSIX_MT) && !defined(SOLARIS_MT)

globaldata global_g;

#else

int numthreads = 0;

struct thread_param_t
{
    population* pop;
    int startidx;
    int endidx;
    globaldata g;
};

#ifdef POSIX_MT

#include <pthread.h>

static pthread_key_t g_key;
static pthread_attr_t pthread_attr;
#endif

#ifdef SOLARIS_MT
#include <thread.h>
static thread_key_t         g_key;
#endif

#endif /* !defined(POSIX_MT) && !defined(SOLARIS_MT) */

/* run_gp()
 *
 * the whole enchilada.  runs, from generation startgen, using population
 * mpop.  accumulates time spent evaluating and breeding in t_eval and t_breed.
 */

void run_gp(multipop* mpop, int startgen,
            event* t_eval, event* t_breed, int startfromcheckpoint)
{
    
    char* param;
    int gen;
    int maxgen;
    int exch_gen;
    int i, j;
    int checkinterval;
    char* checkfileformat;
    char* checkfilename = NULL;
    event start, end, diff;
    int term = 0;
    int stt_interval;
    int bestn;
    
    if (!startfromcheckpoint)
    {
        
        /* get the number of top individuals to track. */
        bestn = atoi(get_parameter("output.bestn"));
        if (bestn < 1)
        {
            error(E_WARNING, "\"output.bestn\" must be at least 1.  defaulting to 1.");
            bestn = 1;
        }
        
        /* allocate statistics for overall run. */
        run_stats = (popstats*) MALLOC((mpop->size + 1) * sizeof(popstats));
        for (i = 0; i < mpop->size + 1; ++i)
        {
            run_stats[i].bestn = bestn;
            run_stats[i].size = -1;
        }
        
        /* initialize the linked list of saved individuals. */
        saved_head = (saved_ind*) MALLOC(sizeof(saved_ind));
        saved_head->ind = NULL;
        saved_head->refcount = 0;
        saved_head->next = NULL;
        saved_tail = saved_head;
    }
    
    /* get the maximum number of generations. */
    param = get_parameter("max_generations");
    if (param == NULL)
        error(E_FATAL_ERROR,
              "no value specified for \"max_generations\".");
    maxgen = atoi(param);
    if (maxgen <= 0)
        error(E_FATAL_ERROR,
              "\"max_generations\" must be greater than zero.");
    
    /* get the interval for subpopulation exchanges, if there is more than
       one subpopulation. */
    if (mpop->size > 1)
    {
        param = get_parameter("multiple.exch_gen");
        if (param == NULL)
            error(E_FATAL_ERROR,
                  "no value specified for \"multiple.exch_gen\".");
        exch_gen = atoi(param);
        if (exch_gen <= 0)
            error(E_FATAL_ERROR,
                  "\"multiple.exch_gen\" must be greater than zero.");
    }
    
    /* get the interval for doing checkpointing. */
    param = get_parameter("checkpoint.interval");
    if (param == NULL)
        /* checkpointing disabled. */
        checkinterval = -1;
    else
        checkinterval = atoi(param);
    
    /* get the format string for the checkpoint filenames. */
    checkfileformat = get_parameter("checkpoint.filename");
    checkfilename = (char*) MALLOC(strlen(checkfileformat) + 50);
    
    /* get the interval for writing information to the .stt file. */
    stt_interval = atoi(get_parameter("output.stt_interval"));
    if (stt_interval < 1)
        error(E_FATAL_ERROR,
              "\"output.stt_interval\" must be greater than zero.");
    
    oputs(OUT_SYS, 10, "\n\nstarting evolution.\n");
    
    /* print out how often we'll be doing checkpointing. */
    if (checkinterval > 0)
        oprintf(OUT_SYS, 20,
                "checkpointing will be done every %d generations and "\
          "after the last generation.\n", checkinterval);
    else if (checkinterval == 0)
        oprintf(OUT_SYS, 20,
                "checkpointing will be done only after the last "\
          "generation.\n");
    else
        oprintf(OUT_SYS, 20,
                "no checkpointing will be done.\n");
    
    output_stream_open(OUT_STT);
    oprintf(OUT_STT, 50, "GEN#\tSUB#\tμFGEN\tFsBestGEN\tFsWorstGEN\tμTreeSzGEN\tμTreeDpGEN\tbTreeSzGEN\tbTreeDpGEN\twTreeSzGEN\twTreeDpGEN\tμFRUN\t"
                         "FsBestRUN\tFsWorstRUN\tμTreeSzRUN\tμTreeDpRUN\tbTreeSzRUN\tbTreeDpRUN\twTreeSzRUN\twTreeDpRUN\n");
    
    /* the big loop. */
    for (gen = startgen; gen <= maxgen && !term; ++gen)
    {
        term = app_begin_of_evaluation(gen, mpop);

        if (term)
            break;
        oprintf(OUT_SYS, 20,
                "=== generation %d.\n", gen);
        
        /* unless this is the first generation after loading a checkpoint
       file... */
        if (!(startfromcheckpoint && gen == startgen))
        {
            
            /* evaluate the population. */
            event_mark(&start);
            for (i = 0; i < mpop->size; ++i)
                evaluate_pop(mpop->pop[i]);
            event_mark(&end);
            event_diff(&diff, &start, &end);

#ifdef TIMING_AVAILABLE
            oprintf(OUT_SYS, 40, "    evaluation complete.  (%s)\n",
                    event_string(&diff));
#else
            oprintf ( OUT_SYS, 40, "    evaluation complete.\n" );
#endif
            
            event_accum(t_eval, &diff);
            
            /* calculate and print statistics.  returns 1 if user termination
               criterion was met, 0 otherwise. */
            term = generation_information(gen, mpop, stt_interval,
                                          run_stats[0].bestn);
            if (term)
                oprintf(OUT_SYS, 30, "user termination criterion met.\n");
            
            flush_output_streams();
            
        }
        
        /** write a checkpoint file if checkinterval is non-negative and:
      we've reached the last generation, or
      the user termination criterion has been met, or
      we've reached the specified checkpoint interval. **/
        if (checkinterval >= 0 &&
            (gen == maxgen || term ||
             (checkinterval > 0 && gen > startgen && (gen % checkinterval) == 0)))
        {
            sprintf(checkfilename, checkfileformat, gen);
            write_checkpoint(gen, mpop, checkfilename);
        }
        
        /** if this is not the last generation and the user criterion hasn't
      been met, then do breeding. **/
        if (gen != maxgen && !term)
        {
            
            /** exchange subpops if it's time. **/
            if (mpop->size > 1 && gen && (gen % exch_gen) == 0)
            {
                exchange_subpopulations(mpop);
                oprintf(OUT_SYS, 10,
                        "    subpopulation exchange complete.\n");
            }
            
            /* breed the new population. */
            event_mark(&start);
            for (i = 0; i < mpop->size; ++i)
                mpop->pop[i] = change_population(mpop->pop[i], mpop->bpt[i]);
            event_mark(&end);
            event_diff(&diff, &start, &end);
            
            /* call the application end-of-breeding callback. */
            app_end_of_breeding(gen, mpop);

#ifdef TIMING_AVAILABLE
            oprintf(OUT_SYS, 30, "    breeding complete.    (%s)\n",
                    event_string(&diff));
#else
            oprintf ( OUT_SYS, 30, "    breeding complete.\n" );
#endif
            
            event_accum(t_breed, &diff);
            
        }
        
        /* free unused ERCs. */
        ephem_const_gc();
        
        flush_output_streams();
        
    }
    
    /** free up a lot of stuff before returning. */
    
    if (checkfilename)
        FREE(checkfilename);
    
    ephem_const_gc();
    
    for (i = 0; i < mpop->size + 1; ++i)
    {
        for (j = 0; j < run_stats[i].bestn; ++j)
            --run_stats[i].best[j]->refcount;
        FREE(run_stats[i].best);
    }
    FREE(run_stats);
    
    saved_individual_gc();
    FREE(saved_head);
}

/* generation_information()
 *
 * calculates and prints population statistics.
 */

int generation_information(int gen, multipop* mpop, int stt_interval,
                           int bestn)
{
    int i, j;
    int newbest;
    static int fd = -1;
    popstats* gen_stats;
    int ret = 0;
    FILE* bout, * hout;
    
    /* number of decimal digits to use when printing fitness values. */
    if (fd == -1)
        fd = atoi(get_parameter("output.digits"));
    
    /* allocate stats records for the current generation. */
    gen_stats = (popstats*) MALLOC((mpop->size + 1) * sizeof(popstats));
    for (i = 0; i < mpop->size + 1; ++i)
    {
        gen_stats[i].bestn = bestn;
        gen_stats[i].size = -1;
    }
    
    oprintf(OUT_GEN, 90, "=== GENERATION %d ===\n", gen);
    oprintf(OUT_PRG, 90, "=== GENERATION %d ===\n", gen);
    
    /* for each subpopulation... */
    for (i = 0; i < mpop->size; ++i)
    {
        /* calculate stats for subpopulation. */
        calculate_pop_stats(gen_stats + i + 1, mpop->pop[i], gen, i);
        /* accumulate that into stats for whole popluation... */
        accumulate_pop_stats(gen_stats, gen_stats + i + 1);
        /* ...and stats for this subpopulation over the whole run. */
        accumulate_pop_stats(run_stats + i + 1, gen_stats + i + 1);
        
        /* if only one subpop, don't print out the subpop stuff. */
        if (mpop->size == 1)
            continue;
        
        /** print much stuff to .gen, .prg, and .stt files. */
        
        if (test_detail_level(90))
        {
            oprintf(OUT_GEN, 90, "    subpopulation %d:\n", i + 1);
            oprintf(OUT_GEN, 90, "        generation:\n");
            oprintf(OUT_GEN, 90, "            mean:   nodes: %.3lf (%d-%d); depth: %.3lf (%d-%d)\n",
                    (double) gen_stats[i + 1].totalnodes / gen_stats[i + 1].size,
                    gen_stats[i + 1].minnodes, gen_stats[i + 1].maxnodes,
                    (double) gen_stats[i + 1].totaldepth / gen_stats[i + 1].size,
                    gen_stats[i + 1].mindepth, gen_stats[i + 1].maxdepth);
            oprintf(OUT_GEN, 90, "            best:   nodes: %d; depth: %d\n",
                    gen_stats[i + 1].bestnodes, gen_stats[i + 1].bestdepth);
            oprintf(OUT_GEN, 90, "            worst:  nodes: %d; depth: %d\n",
                    gen_stats[i + 1].worstnodes, gen_stats[i + 1].worstdepth);
            oprintf(OUT_GEN, 90, "        run:   (%d trees)\n",
                    run_stats[i + 1].size);
            oprintf(OUT_GEN, 90, "            mean:   nodes: %.3lf (%d-%d); depth: %.3lf (%d-%d)\n",
                    (double) run_stats[i + 1].totalnodes / run_stats[i + 1].size,
                    run_stats[i + 1].minnodes, run_stats[i + 1].maxnodes,
                    (double) run_stats[i + 1].totaldepth / run_stats[i + 1].size,
                    run_stats[i + 1].mindepth, run_stats[i + 1].maxdepth);
            oprintf(OUT_GEN, 90, "            best:   nodes: %d; depth: %d\n",
                    run_stats[i + 1].bestnodes, run_stats[i + 1].bestdepth);
            oprintf(OUT_GEN, 90, "            worst:  nodes: %d; depth: %d\n",
                    run_stats[i + 1].worstnodes, run_stats[i + 1].worstdepth);
        }
        
        if (test_detail_level(90))
        {
            oprintf(OUT_PRG, 90, "    subpopulation %d:\n", i + 1);
            oprintf(OUT_PRG, 90, "        generation stats:\n");
            oprintf(OUT_PRG, 90, "            mean:   hits: %.3lf (%d-%d); standardized fitness: %.*lf\n",
                    (double) gen_stats[i + 1].totalhits / gen_stats[i + 1].size,
                    gen_stats[i + 1].minhits, gen_stats[i + 1].maxhits,
                    fd, (double) gen_stats[i + 1].totalfit / gen_stats[i + 1].size);
            oprintf(OUT_PRG, 90, "            best:   hits: %d; standardized fitness: %.*lf\n",
                    gen_stats[i + 1].besthits, fd,
                    (double) gen_stats[i + 1].bestfit);
            oprintf(OUT_PRG, 90, "            worst:  hits: %d; standardized fitness: %.*lf\n",
                    gen_stats[i + 1].worsthits, fd,
                    (double) gen_stats[i + 1].worstfit);
            oprintf(OUT_PRG, 90, "        run stats:     (%d trees)\n",
                    run_stats[i + 1].size);
            oprintf(OUT_PRG, 90, "            mean:   hits: %.3lf (%d-%d); standardized fitness: %.*lf\n",
                    (double) run_stats[i + 1].totalhits / run_stats[i + 1].size,
                    run_stats[i + 1].minhits, run_stats[i + 1].maxhits,
                    fd, (double) run_stats[i + 1].totalfit / run_stats[i + 1].size);
            oprintf(OUT_PRG, 90, "            best:   hits: %d; standardized fitness: %.*lf; generation: %d\n",
                    run_stats[i + 1].besthits, fd, (double) run_stats[i + 1].bestfit,
                    run_stats[i + 1].bestgen);
            oprintf(OUT_PRG, 90, "            worst:  hits: %d; standardized fitness: %.*lf; generation: %d\n",
                    run_stats[i + 1].worsthits, fd,
                    (double) run_stats[i + 1].worstfit, run_stats[i + 1].worstgen);
        }
        
        if (gen % stt_interval == 0)
        {
            oprintf(OUT_STT, 50, "%d\t%d\t", gen, i + 1);
            oprintf(OUT_STT, 50, "%.*lf\t%.*lf %.*lf\t",
                    fd, gen_stats[i + 1].totalfit / gen_stats[i + 1].size,
                    fd, gen_stats[i + 1].bestfit,
                    fd, gen_stats[i + 1].worstfit);
            oprintf(OUT_STT, 50, "%.3lf\t%.3lf\t%d\t%d\t%d\t%d\t",
                    (double) gen_stats[i + 1].totalnodes / gen_stats[i + 1].size,
                    (double) gen_stats[i + 1].totaldepth / gen_stats[i + 1].size,
                    gen_stats[i + 1].bestnodes, gen_stats[i + 1].bestdepth,
                    gen_stats[i + 1].worstnodes, gen_stats[i + 1].worstdepth);
            oprintf(OUT_STT, 50, "%.*lf\t%.*lf\t%.*lf\t",
                    fd, run_stats[i + 1].totalfit / run_stats[i + 1].size,
                    fd, run_stats[i + 1].bestfit,
                    fd, run_stats[i + 1].worstfit);
            oprintf(OUT_STT, 50, "%.3lf\t%.3lf\t%d\t%d\t%d\t%d\t",
                    (double) run_stats[i + 1].totalnodes / run_stats[i + 1].size,
                    (double) run_stats[i + 1].totaldepth / run_stats[i + 1].size,
                    run_stats[i + 1].bestnodes, run_stats[i + 1].bestdepth,
                    run_stats[i + 1].worstnodes, run_stats[i + 1].worstdepth);
            oprintf(OUT_STT, 50, "\n");
        }
        
    }
    
    /* merge stats for current generation into overall run stats. */
    newbest = accumulate_pop_stats(run_stats, gen_stats);
    
    /** more printing. **/
    
    if (test_detail_level(90))
    {
        oprintf(OUT_GEN, 90, "    total population:\n");
        oprintf(OUT_GEN, 90, "        generation:\n");
        oprintf(OUT_GEN, 90, "            mean:   nodes: %.3lf (%d-%d); depth: %.3lf (%d-%d)\n",
                (double) gen_stats[0].totalnodes / gen_stats[0].size,
                gen_stats[0].minnodes, gen_stats[0].maxnodes,
                (double) gen_stats[0].totaldepth / gen_stats[0].size,
                gen_stats[0].mindepth, gen_stats[0].maxdepth);
        oprintf(OUT_GEN, 90, "            best:   nodes: %d; depth: %d\n",
                gen_stats[0].bestnodes, gen_stats[0].bestdepth);
        oprintf(OUT_GEN, 90, "            worst:  nodes: %d; depth: %d\n",
                gen_stats[0].worstnodes, gen_stats[0].worstdepth);
        oprintf(OUT_GEN, 90, "        run:    (%d trees)\n",
                run_stats[0].size);
        oprintf(OUT_GEN, 90, "            mean:   nodes: %.3lf (%d-%d); depth: %.3lf (%d-%d)\n",
                (double) run_stats[0].totalnodes / run_stats[0].size,
                run_stats[0].minnodes, run_stats[0].maxnodes,
                (double) run_stats[0].totaldepth / run_stats[0].size,
                run_stats[0].mindepth, run_stats[0].maxdepth);
        oprintf(OUT_GEN, 90, "            best:   nodes: %d; depth: %d\n",
                run_stats[0].bestnodes, run_stats[0].bestdepth);
        oprintf(OUT_GEN, 90, "            worst:  nodes: %d; depth: %d\n",
                run_stats[0].worstnodes, run_stats[0].worstdepth);
    }
    
    if (test_detail_level(90))
    {
        oprintf(OUT_PRG, 90, "    total population:\n");
        oprintf(OUT_PRG, 90, "        generation stats:\n");
        oprintf(OUT_PRG, 90, "            mean:   hits: %.3lf (%d-%d); standardized fitness: %.*lf\n",
                (double) gen_stats[0].totalhits / gen_stats[0].size,
                gen_stats[0].minhits, gen_stats[0].maxhits,
                fd, (double) gen_stats[0].totalfit / gen_stats[0].size);
        oprintf(OUT_PRG, 90, "            best:   hits: %d; standardized fitness: %.*lf\n",
                gen_stats[0].besthits, fd, (double) gen_stats[0].bestfit);
        oprintf(OUT_PRG, 90, "            worst:  hits: %d; standardized fitness: %.*lf\n",
                gen_stats[0].worsthits, fd, (double) gen_stats[0].worstfit);
        oprintf(OUT_PRG, 90, "        run stats:     (%d trees)\n",
                run_stats[0].size);
        oprintf(OUT_PRG, 90, "            mean:   hits: %.3lf (%d-%d); standardized fitness: %.*lf\n",
                (double) run_stats[0].totalhits / run_stats[0].size,
                run_stats[0].minhits, run_stats[0].maxhits,
                fd, (double) run_stats[0].totalfit / run_stats[0].size);
        oprintf(OUT_PRG, 90, "            best:   hits: %d; standardized fitness: %.*lf; generation: %d\n",
                run_stats[0].besthits, fd, (double) run_stats[0].bestfit,
                run_stats[0].bestgen);
        oprintf(OUT_PRG, 90, "            worst:  hits: %d; standardized fitness: %.*lf; generation: %d\n",
                run_stats[0].worsthits, fd, (double) run_stats[0].worstfit,
                run_stats[0].worstgen);
    }
    
    if (gen % stt_interval == 0)
    {
        if (test_detail_level(50))
        {
            oprintf(OUT_STT, 50, "%d\t0\t", gen);
            oprintf(OUT_STT, 50, "%.*lf\t%.*lf\t%.*lf\t",
                    fd, gen_stats[0].totalfit / gen_stats[0].size,
                    fd, gen_stats[0].bestfit, fd, gen_stats[0].worstfit);
            oprintf(OUT_STT, 50, "%.3lf\t%.3lf\t%d\t%d\t%d\t%d\t",
                    (double) gen_stats[0].totalnodes / gen_stats[0].size,
                    (double) gen_stats[0].totaldepth / gen_stats[0].size,
                    gen_stats[0].bestnodes, gen_stats[0].bestdepth,
                    gen_stats[0].worstnodes, gen_stats[0].worstdepth);
            oprintf(OUT_STT, 50, "%.*lf\t%.*lf\t%.*lf\t",
                    fd, run_stats[0].totalfit / run_stats[0].size,
                    fd, run_stats[0].bestfit, fd, run_stats[0].worstfit);
            oprintf(OUT_STT, 50, "%.3lf\t%.3lf\t%d\t%d\t%d\t%d\t",
                    (double) run_stats[0].totalnodes / run_stats[0].size,
                    (double) run_stats[0].totaldepth / run_stats[0].size,
                    run_stats[0].bestnodes, run_stats[0].bestdepth,
                    run_stats[0].worstnodes, run_stats[0].worstdepth);
            oprintf(OUT_STT, 50, "\n");
        }
    }
    
    /* rewrite the .bst file, and append to the .his file. */
    
    output_stream_open(OUT_BST);
    
    oprintf(OUT_BST, 10, "=== BEST-OF-RUN ===\n");
    oprintf(OUT_BST, 10, "              generation: %d\n",
            run_stats[0].bestgen);
    if (mpop->size > 1)
        oprintf(OUT_BST, 10, "           subpopulation: %d\n",
                run_stats[0].bestpop + 1);
    oprintf(OUT_BST, 10, "                   nodes: %d\n",
            run_stats[0].bestnodes);
    oprintf(OUT_BST, 10, "                   depth: %d\n",
            run_stats[0].bestdepth);
    oprintf(OUT_BST, 10, "                    hits: %d\n",
            run_stats[0].besthits);
    
    oprintf(OUT_HIS, 10, "=== BEST-OF-RUN ===\n");
    oprintf(OUT_HIS, 10, "      current generation: %d\n", gen);
    oprintf(OUT_HIS, 10, "              generation: %d\n",
            run_stats[0].bestgen);
    if (mpop->size > 1)
        oprintf(OUT_HIS, 10, "           subpopulation: %d\n",
                run_stats[0].bestpop + 1);
    oprintf(OUT_HIS, 10, "                   nodes: %d\n",
            run_stats[0].bestnodes);
    oprintf(OUT_HIS, 10, "                   depth: %d\n",
            run_stats[0].bestdepth);
    oprintf(OUT_HIS, 10, "                    hits: %d\n",
            run_stats[0].besthits);
    
    /* retrieve the (FILE *) for the .bst and .his files, so that
       the trees can be printed to them. */
    
    bout = output_filehandle(OUT_BST);
    hout = output_filehandle(OUT_HIS);
    
    if (run_stats[0].bestn == 1)
    {
        oprintf(OUT_BST, 20, "TOP INDIVIDUAL:\n\n");
        oprintf(OUT_HIS, 20, "TOP INDIVIDUAL:\n\n");
    } else
    {
        oprintf(OUT_BST, 20, "TOP %d INDIVIDUALS (in order):\n\n",
                run_stats[0].bestn);
        oprintf(OUT_HIS, 20, "TOP %d INDIVIDUALS (in order):\n\n",
                run_stats[0].bestn);
    }
    
    for (i = 0; i < run_stats[0].bestn; ++i)
    {
        oprintf(OUT_BST, 20, "\n\n-- #%d --\n", i + 1);
        
        oprintf(OUT_BST, 20, "                    hits: %d\n",
                run_stats[0].best[i]->ind->hits);
        oprintf(OUT_BST, 20, "             raw fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->r_fitness);
        oprintf(OUT_BST, 20, "    standardized fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->s_fitness);
        oprintf(OUT_BST, 20, "        adjusted fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->a_fitness);
        
        oprintf(OUT_HIS, 20, "\n\n-- #%d --\n", i + 1);
        
        oprintf(OUT_HIS, 20, "                    hits: %d\n",
                run_stats[0].best[i]->ind->hits);
        oprintf(OUT_HIS, 20, "             raw fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->r_fitness);
        oprintf(OUT_HIS, 20, "    standardized fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->s_fitness);
        oprintf(OUT_HIS, 20, "        adjusted fitness: %.*lf\n",
                fd, run_stats[0].best[i]->ind->a_fitness);
        
        /* print the tree to both files here. */
        if (test_detail_level(20))
        {
            pretty_print_individual(run_stats[0].best[i]->ind, bout);
            pretty_print_individual_equ(run_stats[0].best[i]->ind, bout);
            pretty_print_individual(run_stats[0].best[i]->ind, hout);
        }
    }
    
    /* call the end-of-evaluation callback.  returns 1 if user termination
       criterion is met, 0 otherwise. */
    ret = app_end_of_evaluation(gen, mpop, newbest, gen_stats, run_stats);
    
    /* close the .bst file. */
    output_stream_close(OUT_BST);
    
    /* free stats structures for current generation. */
    for (i = 0; i < mpop->size + 1; ++i)
    {
        for (j = 0; j < gen_stats[i].bestn; ++j)
            --gen_stats[i].best[j]->refcount;
        FREE(gen_stats[i].best);
    }
    FREE(gen_stats);
    
    /* deallocate saved individuals that are no longer needed. */
    saved_individual_gc();
    
    /* return value the application callback gave us. */
    return ret;
    
}

/* evaluate_pop()
 *
 * evaluates all the individuals in a population whose cached
 * fitness values are invalid.
 */

void evaluate_pop(population* pop)
{
    int i;
#if defined(POSIX_MT) || defined(SOLARIS_MT)
    int start, end, inc, err;
    struct thread_param_t* t_param;
#endif
#if POSIX_MT
    pthread_t* t_ids;
#endif

#ifdef DEBUG
    print_individual ( pop->ind, stdout );
#ifdef COEVOLUTION
  printf("Can't do COEVOLUTION and DEBUG at the same time, sorry!\n");
#else
    app_eval_fitness ( pop->ind );
#endif
    exit(0);
#endif

#if !defined(POSIX_MT) && !defined(SOLARIS_MT)
#ifdef COEVOLUTION
    
    if (pop->size % 2)
    {
        /* It's not even! */
          error ( E_FATAL_ERROR, "Population must be even to do COEVOLUTION\n");
    }
    
      for ( i = 0; i < pop->size; i+=2)
        if ( pop->ind[i].evald != EVAL_CACHE_VALID ||
             pop->ind[i+1].evald != EVAL_CACHE_VALID )
          app_eval_fitness ( (pop->ind)+i );
#else
      for ( i = 0; i < pop->size; ++i )
        if ( pop->ind[i].evald != EVAL_CACHE_VALID )
          app_eval_fitness ( (pop->ind)+i );
#endif

#else

#if POSIX_MT
    t_ids = (pthread_t*) MALLOC(numthreads * sizeof(pthread_t));
#endif
    t_param = (struct thread_param_t*) MALLOC(numthreads *
                                              sizeof(struct thread_param_t));
    
    /* figure out how many pop members per thread */
    inc = pop->size / numthreads;
    if (pop->size != inc * numthreads) inc++;
    
    start = 0;
    for (i = 0; i < numthreads; i++)
    {
        end = start + inc;
        if (end > pop->size) end = pop->size;
        
        /* setup the paramater to pass */
        t_param[i].pop = pop;
        t_param[i].startidx = start;
        t_param[i].endidx = end;
        t_param[i].g = *(get_globaldata());

#ifdef POSIX_MT
        err = pthread_create(&t_ids[i], &pthread_attr,
                             evaluate_pop_chunk, &t_param[i]);
#endif
#ifdef SOLARIS_MT
        err = thr_create(NULL, (int)NULL, evaluate_pop_chunk,
                 &t_param[i], (int)NULL,NULL);
#endif
        if (err != 0)
        {
            error(E_FATAL_ERROR, "cannot create thread");
        }
        
        start = end;
    }

#ifdef SOLARIS_MT
    while (thr_join(NULL, NULL, NULL) == 0);
#endif
#ifdef POSIX_MT
    for (i = 0; i < numthreads; i++)
    {
        pthread_join(t_ids[i], NULL);
    }
    FREE(t_ids);
#endif
    FREE(t_param);

#endif

}

/* calculate_pop_stats()
 *
 * tabulates stats for a population:  fitness and size of best, worst,
 * mean, etc.  also finds top N individuals and saves them.
 */

void calculate_pop_stats(popstats* s, population* pop, int gen,
                         int subpop)
{
    int i, j, k, l;
    int b;
    saved_ind* shp;
    individual** temp;
    
    /* allocate a list of the top N individuals. */
    s->best = (saved_ind**) MALLOC(s->bestn *
                                   sizeof(saved_ind*));
    temp = (individual**) MALLOC((s->bestn + 1) * sizeof(individual*));
    
    s->size = pop->size;
    
    /** this is all pretty obvious -- set all the max and min values to the
      first individual's values, then go through the population looking for
      things that are bigger/smaller/better/worse/etc. **/
    
    s->maxnodes = s->minnodes = s->totalnodes = s->bestnodes = s->worstnodes =
            individual_size(pop->ind + 0);
    s->maxdepth = s->mindepth = s->totaldepth = s->bestdepth = s->worstdepth =
            individual_depth(pop->ind + 0);
    s->maxhits = s->minhits = s->totalhits = s->besthits = s->worsthits =
            pop->ind[0].hits;
    s->bestfit = s->worstfit = s->totalfit = pop->ind[0].a_fitness;
    temp[0] = pop->ind;
    b = 1;
    s->bestgen = s->worstgen = gen;
    s->bestpop = s->worstpop = subpop;
    
    for (i = 1; i < s->size; ++i)
    {
        j = individual_size(pop->ind + i);
        s->totalnodes += j;
        if (j < s->minnodes) s->minnodes = j;
        if (j > s->maxnodes) s->maxnodes = j;
        
        k = individual_depth(pop->ind + i);
        s->totaldepth += k;
        if (k < s->mindepth) s->mindepth = k;
        if (k > s->maxdepth) s->maxdepth = k;
        
        l = pop->ind[i].hits;
        s->totalhits += l;
        if (l < s->minhits) s->minhits = l;
        if (l > s->maxhits) s->maxhits = l;
        
        s->totalfit += pop->ind[i].a_fitness;
        if (pop->ind[i].a_fitness > s->bestfit)
        {
            s->bestfit = pop->ind[i].a_fitness;
            s->bestnodes = j;
            s->bestdepth = k;
            s->besthits = l;
        } else if (pop->ind[i].a_fitness < s->worstfit)
        {
            s->worstfit = pop->ind[i].a_fitness;
            s->worstnodes = j;
            s->worstdepth = k;
            s->worsthits = l;
        }
        
        /** insert the current individual into the top N list
      (if it belongs there). **/
        
        for (j = b; j > 0; --j)
        {
            if (pop->ind[i].a_fitness < temp[j - 1]->a_fitness)
                break;
            temp[j] = temp[j - 1];
        }
        if (j < s->bestn)
            temp[j] = pop->ind + i;
        if (b < s->bestn)
            ++b;
    }
    
    /** now save copies of the individuals in the "temp" list **/
    for (i = 0; i < b; ++i)
    {
        shp = (saved_ind*) MALLOC(sizeof(saved_ind));
        shp->ind = (individual*) MALLOC(sizeof(individual));
        shp->ind->tr = (tree*) MALLOC(tree_count * sizeof(tree));
        duplicate_individual(shp->ind, temp[i]);
        for (j = 0; j < tree_count; ++j)
            reference_ephem_constants(shp->ind->tr[j].data, 1);
        shp->refcount = 1;
        shp->next = NULL;
        
        saved_tail->next = shp;
        saved_tail = shp;
        ++saved_head->refcount;
        
        s->best[i] = shp;
    }

#ifdef DEBUG
    printf ( "the best list is:\n" );
    for ( j = 0; j < s->bestn; ++j )
      printf ( "     %08x  %lf\n", s->best[j], s->best[j]->ind->a_fitness );
#endif
    
    FREE(temp);
    
}

/* accumulate_pop_stats()
 *
 * this merges the second statistics record into the first, so that it reflects
 * the "sum" of the underlying populations.  returns 1 if the best individual
 * of the first record has changed (that is, if the second record has a better
 * best individual. 
 */

int accumulate_pop_stats(popstats* total, popstats* n)
{
    int ret = 0;
    int i, j, k;
    saved_ind** temp;
    
    if (total->size == -1)
    {
        /* if the "total" record is empty, then just copy the second record
       into it. */
        memcpy(total, n, sizeof(popstats));
        total->best = (saved_ind**) MALLOC(total->bestn *
                                           sizeof(saved_ind*));
        memcpy(total->best, n->best, total->bestn *
                                     sizeof(saved_ind*));
        ret = 1;
    } else
    {
        /* sum the totals. */
        total->size += n->size;
        total->totalnodes += n->totalnodes;
        total->totaldepth += n->totaldepth;
        total->totalhits += n->totalhits;
        total->totalfit += n->totalfit;
        
        /* find the maximums. */
        if (n->maxnodes > total->maxnodes) total->maxnodes = n->maxnodes;
        if (n->maxdepth > total->maxdepth) total->maxdepth = n->maxdepth;
        if (n->maxhits > total->maxhits) total->maxhits = n->maxhits;
        
        /* find the minimums. */
        if (n->minnodes < total->minnodes) total->minnodes = n->minnodes;
        if (n->mindepth < total->mindepth) total->mindepth = n->mindepth;
        if (n->minhits < total->minhits) total->minhits = n->minhits;
        
        /* find the best individual's numbers. */
        if (n->bestfit > total->bestfit)
        {
            total->bestfit = n->bestfit;
            total->bestnodes = n->bestnodes;
            total->bestdepth = n->bestdepth;
            total->besthits = n->besthits;
            total->bestgen = n->bestgen;
            total->bestpop = n->bestpop;
            ret = 1;
        }
        
        /* find the worst individual's numbers. */
        if (n->worstfit < total->worstfit)
        {
            total->worstfit = n->worstfit;
            total->worstnodes = n->worstnodes;
            total->worstdepth = n->worstdepth;
            total->worsthits = n->worsthits;
            total->worstgen = n->worstgen;
            total->worstpop = n->worstpop;
        }

#ifdef DEBUG
        printf ( "total list:\n" );
        for ( i = 0; i < total->bestn; ++i )
      printf ( "   %08x %lf\n",
           total->best[i], total->best[i]->ind->a_fitness );
        printf ( "new list:\n" );
        for ( i = 0; i < n->bestn; ++i )
      printf ( "   %08x %lf\n",
           n->best[i], n->best[i]->ind->a_fitness );
#endif
        
        /** here we merge the two "top N" lists into one, discarding
      the remaining N individuals. **/
        
        temp = (saved_ind**) MALLOC(total->bestn *
                                    sizeof(saved_ind*));
        j = 0;   /* position in "total"s list */
        k = 0;   /* position in "n"s list */
        for (i = 0; i < total->bestn; ++i)
        {
            /* if the n list is empty, take from the total list. */
            if (k == -1)
                temp[i] = total->best[j++];
                /* if the total list is empty, take from the n list. */
            else if (j == -1)
            {
                ret |= (i == 0);
                temp[i] = n->best[k++];
            }
                /* if neither list is empty, take the better individual. */
            else if (total->best[j]->ind->a_fitness <
                     n->best[k]->ind->a_fitness)
            {
                ret |= (i == 0);
                temp[i] = n->best[k++];
            } else
                temp[i] = total->best[j++];
            
            /* have we run off the end of either list? */
            if (j >= total->bestn)
                j = -1;
            if (k >= n->bestn)
                k = -1;
        }
        
        /* decrement the reference count of the old "best" list. */
        for (i = 0; i < total->bestn; ++i)
            --total->best[i]->refcount;
        
        FREE(total->best);
        total->best = temp;

#ifdef DEBUG
        printf ( "new total list:\n" );
        for ( i = 0; i < total->bestn; ++i )
      printf ( "   %08x %lf\n",
           total->best[i], total->best[i]->ind->a_fitness );
#endif
    }
    
    /* increment the reference count of the new "best" list. */
    for (i = 0; i < total->bestn; ++i)
        ++total->best[i]->refcount;
    
    return ret;
}

/* saved_individual_gc()
 *
 * go through the list of saved individuals, deleting any which are no longer
 * referred to. 
 */

void saved_individual_gc(void)
{
    int j;
    saved_ind* shp = saved_head->next;
    saved_ind* shm = saved_head;
    
    while (shp)
    {
        if (shp->refcount == 0)
        {
            /** found one that needs to be deleted. **/
            
            /* dereference its trees' ERCs and delete the trees. */
            for (j = 0; j < tree_count; ++j)
            {
                reference_ephem_constants(shp->ind->tr[j].data, -1);
                free_tree(shp->ind->tr + j);
            }
            FREE(shp->ind->tr);
            FREE(shp->ind);
            
            /* cut the record out of the linked list. */
            shm->next = shp->next;
            if (saved_tail == shp)
                saved_tail = shm;
            FREE(shp);
            shp = shm->next;
            
            /* the refcount field of the list head (a dummy node) holds the
               size of the list. */
            --saved_head->refcount;
        } else
        {
            /* move down the list. */
            shm = shp;
            shp = shp->next;
        }
    }
}

/* read_saved_individuals()
 *
 * reads the list of saved individuals from a checkpoint file.  constructs
 * an index translating indices to addresses.
 */

saved_ind** read_saved_individuals(ephem_const** eind, FILE* f)
{
    char* buffer;
    int count;
    int i;
    saved_ind* p;
    saved_ind** sind;
    
    buffer = (char*) MALLOC(MAXCHECKLINELENGTH);
    
    /* read the number of saved individuals. */
    fscanf(f, "%*s %d\n", &count);
    
    /* allocate the index. */
    sind = (saved_ind**) MALLOC(count * sizeof(saved_ind*));
    
    /* allocate the head of the linked list (a dummy node whose refcount
       equals the number of individuals on the list). */
    saved_head = (saved_ind*) MALLOC(sizeof(saved_ind));
    saved_head->ind = NULL;
    saved_head->refcount = count;
    p = saved_head;
    for (i = 0; i < count; ++i)
    {
        /* allocate the next saved_ind on the list. */
        p->next = (saved_ind*) MALLOC(sizeof(saved_ind));
        p = p->next;
        /* allocate the individual. */
        p->ind = (individual*) MALLOC(sizeof(individual));
        /* make the index entry. */
        sind[i] = p;
        /* read the refcount. */
        fscanf(f, "%d ", &(p->refcount));
        /* read the individual. */
        read_individual(p->ind, eind, f, buffer);
    }
    /* mark the end of the list. */
    p->next = NULL;
    saved_tail = p;
    
    FREE(buffer);
    
    return sind;
}

/* read_stats_checkpoint()
 *
 * read the overall run statistics structures from a checkpoint file.
 */

void read_stats_checkpoint(multipop* mpop, ephem_const** eind, FILE* f)
{
    int i, j, k;
    saved_ind** sind;
    
    /* read and index the saved individuals list. */
    sind = read_saved_individuals(eind, f);
    
    /* allocate the run_stats array. */
    run_stats = (popstats*) MALLOC((mpop->size + 1) * sizeof(popstats));
    for (i = 0; i < mpop->size + 1; ++i)
    {
        /* read lots of integer values into run_stats. */
        fscanf(f, "%d  %d %d %d %d %d  %d %d %d %d %d  %d %d %d %d %d\n",
               &(run_stats[i].size),
               &(run_stats[i].maxnodes), &(run_stats[i].minnodes),
               &(run_stats[i].totalnodes), &(run_stats[i].bestnodes),
               &(run_stats[i].worstnodes),
               &(run_stats[i].maxdepth), &(run_stats[i].mindepth),
               &(run_stats[i].totaldepth), &(run_stats[i].bestdepth),
               &(run_stats[i].worstdepth),
               &(run_stats[i].maxhits), &(run_stats[i].minhits),
               &(run_stats[i].totalhits), &(run_stats[i].besthits),
               &(run_stats[i].worsthits));
        /** double-precision values are stored as hex data to avoid loss
      of precision. **/
        read_hex_block(&(run_stats[i].bestfit), sizeof(double), f);
        fgetc(f);
        read_hex_block(&(run_stats[i].worstfit), sizeof(double), f);
        fgetc(f);
        read_hex_block(&(run_stats[i].totalfit), sizeof(double), f);
        /* they are also printed as decimal values, for the benefit of human
       readers -- skip these fields. */
        fscanf(f, " %*f %*f %*f\n");
        /* read some more integers. */
        fscanf(f, "%d %d %d %d %d ",
               &(run_stats[i].bestgen), &(run_stats[i].worstgen),
               &(run_stats[i].bestpop), &(run_stats[i].worstpop),
               &(run_stats[i].bestn));
        run_stats[i].best = (saved_ind**) MALLOC(run_stats[i].bestn *
                                                 sizeof(saved_ind*));
        /** read the indices of the contents of the best array, and look up
      the addresses in the index. **/
        for (j = 0; j < run_stats[i].bestn; ++j)
        {
            fscanf(f, "%d\n", &k);
            run_stats[i].best[j] = sind[k];
        }
    }
    FREE(sind);
}

/* write_saved_individuals()
 *
 * writes the linked list of saved individuals to a checkpoint file.  returns
 * an index for translating saved_ind addresses to integer indices.
 */

saved_ind** write_saved_individuals(ephem_index* eind, FILE* f)
{
    saved_ind** index;
    saved_ind* shp;
    int i = 0;
    
    index = (saved_ind**) MALLOC(saved_head->refcount *
                                 sizeof(saved_ind*));
    
    /* write the count of individuals. */
    fprintf(f, "saved-individual-count: %d\n", saved_head->refcount);
    
    shp = saved_head->next;
    
    /** traverse the linked list. **/
    while (shp)
    {
        /* write the reference count and individual. */
        fprintf(f, "%d ", shp->refcount);
        write_individual(shp->ind, eind, f);
        
        /* record the address in the index. */
        index[i++] = shp;
        
        shp = shp->next;
    }
    
    return index;
}

/* write_stats_checkpoint()
 *
 * write the overall run statistics structures to a checkpoint file.
 */

void write_stats_checkpoint(multipop* mpop, ephem_index* eind, FILE* f)
{
    int i, j, k;
    saved_ind** sind;
    
    /* write and index the saved individuals list. */
    sind = write_saved_individuals(eind, f);
    
    for (i = 0; i < mpop->size + 1; ++i)
    {
        /* write many integer values. */
        fprintf(f, "%d  %d %d %d %d %d  %d %d %d %d %d  %d %d %d %d %d\n",
                run_stats[i].size,
                run_stats[i].maxnodes, run_stats[i].minnodes,
                run_stats[i].totalnodes, run_stats[i].bestnodes,
                run_stats[i].worstnodes,
                run_stats[i].maxdepth, run_stats[i].mindepth,
                run_stats[i].totaldepth, run_stats[i].bestdepth,
                run_stats[i].worstdepth,
                run_stats[i].maxhits, run_stats[i].minhits,
                run_stats[i].totalhits, run_stats[i].besthits,
                run_stats[i].worsthits);
        /** write double-precision values as hex data. **/
        write_hex_block(&(run_stats[i].bestfit), sizeof(double), f);
        fputc(' ', f);
        write_hex_block(&(run_stats[i].worstfit), sizeof(double), f);
        fputc(' ', f);
        write_hex_block(&(run_stats[i].totalfit), sizeof(double), f);
        /* also write them as decimal values. */
        fprintf(f, " %f %f %f\n", run_stats[i].bestfit,
                run_stats[i].worstfit, run_stats[i].totalfit);
        /* write more integers. */
        fprintf(f, "%d %d %d %d %d ",
                run_stats[i].bestgen, run_stats[i].worstgen,
                run_stats[i].bestpop, run_stats[i].worstpop,
                run_stats[i].bestn);
        /** write the best array, indexing saved individuals using integers. **/
        for (j = 0; j < run_stats[i].bestn; ++j)
        {
            /** search the index for the address. **/
            for (k = 0; k < saved_head->refcount; ++k)
                if (run_stats[i].best[j] == sind[k])
                {
                    /* print the index to the checkpoint file. */
                    fprintf(f, " %d", k);
                    break;
                }
            /** address was not found in the index. **/
            if (k == saved_head->refcount)
            {
                /* this shouldn't ever happen. */
                fprintf(f, " -1");
                error(E_WARNING, "bestn pointer is bad.");
            }
        }
        
        fputc('\n', f);
    }
    
    FREE(sind);
}


#if !defined(POSIX_MT) && !defined(SOLARIS_MT)

/* return the globaldata structure */
globaldata *get_globaldata(void) {
  return( &global_g );
}

#else /* continues to end of file */

/* provide each thread with seperate copy of 'g' */
globaldata* get_globaldata(void)
{
    globaldata* retval;

#ifdef POSIX_MT
    retval = pthread_getspecific(g_key);
#endif
#ifdef SOLARIS_MT
    thr_getspecific( g_key, (void *)&retval );
#endif
    
    if (retval == NULL)
    {
        error(E_FATAL_ERROR, "get_globaldata() tried to return NULL");
    }
    
    return (retval);
}


/*
 * initialize_threading()
 *
 * Setup the program for multithreading use.
 */

void initialize_threading(void)
{
    char* numthreads_str;
    globaldata* main_g;
    
    /* numthreads_str = getenv( "NUM_THREADS" );*/
    numthreads_str = get_parameter("num_threads"); /* Now in input file */
    if (numthreads_str == NULL)
    {
        error(E_FATAL_ERROR, "num_threads undefined");
    }
    numthreads = atoi(numthreads_str);
    if (numthreads < 1)
    {
        error(E_FATAL_ERROR, "num_threads must be > 0");
    }
    
    printf("numthreads: %d\n", numthreads);
    
    /* create the thread key for access to 'g' */
#ifdef POSIX_MT
    pthread_key_create(&g_key, NULL);
#endif
#ifdef SOLARIS_MT
    thr_keycreate( &g_key, NULL );
#endif
    
    /* main thread needs its own 'g' */
    main_g = (globaldata*) malloc(sizeof(globaldata));
#ifdef POSIX_MT
    pthread_setspecific(g_key, main_g);
#endif
#ifdef SOLARIS_MT
    thr_setspecific(g_key, main_g);
#endif

#ifdef POSIX_MT
    /* Setup default thread attributes */
    pthread_attr_init(&pthread_attr);
    pthread_attr_setscope(&pthread_attr, PTHREAD_SCOPE_SYSTEM);
    
    if (1)
    {
        size_t size;
        size_t guard;
        
        /* Double the stack size */
        pthread_attr_getstacksize(&pthread_attr, &size);
        printf("Kernel Old Size %d\n", (int) size);
        pthread_attr_setstacksize(&pthread_attr, size * 2);
        pthread_attr_getstacksize(&pthread_attr, &size);
        printf("Kernel New Size %d\n", (int) size);

/* Double the Guard size (account for old pthread implementation) */
#ifdef PTHREAD_1.X
        pthread_attr_getguardsize_np(&pthread_attr,&guard);
        printf("Kernel Old Guard %d\n",(int)guard);
        pthread_attr_setguardsize_np(&pthread_attr,2*guard);
        pthread_attr_getguardsize_np(&pthread_attr,&guard);
#else
        pthread_attr_getguardsize(&pthread_attr, &guard);
        printf("Kernel Old Guard %d\n", (int) guard);
        pthread_attr_setguardsize(&pthread_attr, 2 * guard);
        pthread_attr_getguardsize(&pthread_attr, &guard);
#endif
        printf("Kernel New Guard %d\n", (int) guard);
    }
#endif

#ifdef SOLARIS_MT
    /* let the OS know how many threads to run at once */
    thr_setconcurrency( numthreads );
#endif

}

/* evaluate_pop_chuck()
 *
 * Called from evaluate_pop to do a chunk of evaluations on a pop.
 * This was done to allow multithreading.
 */

void* evaluate_pop_chunk(void* param)
{
    int k, startidx, endidx;
    population* pop;
    globaldata* g;
    struct thread_param_t* t_param;
    
    t_param = (struct thread_param_t*) param;
    pop = t_param->pop;
    startidx = t_param->startidx;
    endidx = t_param->endidx;

#ifdef POSIX_MT
    pthread_setspecific(g_key, &t_param->g);
#endif
#ifdef SOLARIS_MT
    thr_setspecific(g_key, &t_param->g);
#endif
    
    g = get_globaldata();
    
    /* printf("START: %d,%d\n", startidx, endidx); */

#ifdef COEVOLUTION            /* Here we hack it to provide *two* individuals */
    
    if ((endidx-startidx)%2)  /* it it's not even */
  
      {
        char xx[256];
        sprintf(xx,"Uneven number of individuals (%d) at %d",
            endidx-startidx,startidx);
        error ( E_FATAL_ERROR,xx);
      }
  
    for ( k = startidx; k < endidx; k+=2 )
      
      /* It's GOT to have even number of inds */
  
      if ( pop->ind[k].evald != EVAL_CACHE_VALID ||
       pop->ind[k+1].evald != EVAL_CACHE_VALID )
        {
      app_eval_fitness ( (pop->ind)+k, (pop->ind)+(k+1) );
        }
#else
    for (k = startidx; k < endidx; ++k)
        if (pop->ind[k].evald != EVAL_CACHE_VALID)
        {
            app_eval_fitness((pop->ind) + k);
        }
#endif

}

#endif /* !defined(POSIX_MT) && !defined(SOLARIS_MT) */