/*  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
 *
 *  This parallel version based on PVM is an extension of version 1.1
 *  of Lil-gp.
 *
 *  Johan Parent        (johan@info.vub.ac.be)
 *
 *  Faculty of Applied Sciences (Engineering Faculty)
 *  Building K, Computer Science Department
 *  Vrije Universiteit Brussel
 *  Pleinlaan 2, 1050 Etterbeek
 *  Belgium
 *
 */

#include <lilgp.h>

popstats *run_stats = NULL;
saved_ind *saved_head = NULL;
saved_ind *saved_tail;

#ifdef _PARALLEL_H
int gen;
int term = 0;

event eval;
event breeding;

event eval_diff;
event breed_diff;
#endif


/* 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;
#ifndef _PARALLEL_H 
     int gen;
     int term = 0;
#else
     int low;
     int high;
#endif

     int maxgen;
     int exch_gen;
     int i, j;
     int checkinterval;
     char *checkfileformat;
     char *checkfilename = NULL;
     event start, end, diff;

     int stt_interval;
     int bestn;

#ifdef _PARALLEL_H
     exch_gen = -1;
#endif

     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" );

     /* the big loop. */
     for ( gen = startgen; gen <= maxgen && !term; ++gen )
     {
          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 );

#ifndef _PARALLEL_H
               for ( i = 0; i < mpop->size; ++i )
#else
	       low = get_low();
	       high = get_high();
               for ( i = low; i < high; ++i )
#endif
                    evaluate_pop ( mpop->pop[i] );

               event_mark ( &end );
               event_diff ( &diff, &start, &end );
#ifdef _PARALLEL_H
	       eval_diff = diff;
#endif

#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" );
#ifdef _PARALLEL_H
		    if (get_operation_mode() == SLAVE)
			{
		    	/* Send termination signal to the master */
		    	signal_master(TERM_CRITERION_TAG);
			}
#endif
	       }
               
               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 );
#ifndef _PARALLEL_H
               for ( i = 0; i < mpop->size; ++i )
#else
	       low = get_low();
	       high = get_high();
               for ( i = low; i < high; ++i )
#endif
                    mpop->pop[i] = change_population ( mpop->pop[i], mpop->bpt[i] );
               event_mark ( &end );
               event_diff ( &diff, &start, &end );

#ifdef _PARALLEL_H
	       breed_diff = diff;
#endif


	       /* 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();

#ifndef _PARALLEL_H
     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 );
     }
#else
     low = get_low();
     high = get_high();

     /* We used only a part of the stats */
     for ( i =  low+1; i < high+1; ++i )
     {
	if (run_stats[i].best)
	  {
          for ( j = 0; j < run_stats[i].bestn; ++j )
        	--run_stats[i].best[j]->refcount;

      	  FREE ( run_stats[i].best );
	  }

     }
     /* Last but not least */
     for ( j = 0; j < run_stats[0].bestn; ++j )
          --run_stats[0].best[j]->refcount;
     FREE ( run_stats[0].best );
#endif

     FREE ( run_stats );

     saved_individual_gc();
     FREE ( saved_head );

#ifdef _PARALLEL_H
     if (get_operation_mode() == SLAVE)
	{
	/* Send termination signal to the master */
	signal_master(TERM_ENDED_TAG);
	}
#endif

}

/* 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;
#ifdef _PARALLEL_H
     int low;
     int high;
#endif

#ifdef _PARALLEL_H
    if (get_operation_mode() == SLAVE)
	{
    	/* Send information to the master */
	return send_generation_information ( gen, mpop, stt_interval, bestn );
	}
#endif


     /* number of decimal digits to use when printing fitness values. */
     if ( fd == -1 )
          fd = atoi ( get_parameter ( "output.digits" ) );

#ifdef PDEBUG_GP
printf("Begin generation_info\n"); fflush(stdout);
#endif

     /* 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... */
#ifndef _PARALLEL_H
     for ( i = 0; i < mpop->size; ++i )
#else
     low = get_low();
     high = get_high();
     for ( i = low; i < high; ++i )
#endif
     {
#ifdef PDEBUG_GP
printf("gne_info = %d\n", i); fflush(stdout);
#endif
	  /* 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 %d ", gen, i+1 );
               oprintf ( OUT_STT, 50, "%.*lf %.*lf %.*lf ",
                        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 %.3lf %d %d %d %d ",
                        (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 %.*lf %.*lf ",
                        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 %.3lf %d %d %d %d ",
                        (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 0 ", gen );
               oprintf ( OUT_STT, 50, "%.*lf %.*lf %.*lf ",
                        fd, gen_stats[0].totalfit/gen_stats[0].size,
                        fd, gen_stats[0].bestfit, fd, gen_stats[0].worstfit );
               oprintf ( OUT_STT, 50, "%.3lf %.3lf %d %d %d %d ",
                        (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 %.*lf %.*lf ",
                        fd, run_stats[0].totalfit/run_stats[0].size,
                        fd, run_stats[0].bestfit, fd, run_stats[0].worstfit );
               oprintf ( OUT_STT, 50, "%.3lf %.3lf %d %d %d %d ",
                        (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. */
     if (get_operation_mode() != SLAVE)
     	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 ( 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. */
     if (get_operation_mode() != SLAVE)
     	output_stream_close ( OUT_BST );

     /* free stats structures for current generation. */
#ifndef _PARALLEL_H
     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 );
     }
#else
     low = get_low();
     high = get_high();

     /* We used only a part of the stats */
     for ( i =  low+1; i < high+1; ++i )
     {
          for ( j = 0; j < gen_stats[i].bestn; ++j )
               --gen_stats[i].best[j]->refcount;
          FREE ( gen_stats[i].best );
     }
     /* Last but not least */
     for ( j = 0; j < gen_stats[0].bestn; ++j )
          --gen_stats[0].best[j]->refcount;

     FREE ( gen_stats[0].best );
#endif
     FREE ( gen_stats );

     /* deallocate saved individuals that are no longer needed. */
     saved_individual_gc();

#ifdef PDEBUG_GP
printf("Finished generation_info()\n"); fflush(stdout);
#endif

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

#ifdef DEBUG
     print_individual ( pop->ind, stdout );
     app_eval_fitness ( pop->ind );
     exit(0);
#endif

     
     for ( k = 0; k < pop->size; ++k )
          if ( pop->ind[k].evald != EVAL_CACHE_VALID )
               app_eval_fitness ( (pop->ind)+k );
}

/* 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 _PARALLEL_H
     s->real_size = b;
#endif

#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;

//printf("refcount head = %d\n", shm->refcount);

     while ( shp )
     {
//printf("refcount = %d\n", shp->refcount);
          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 );
	       /* bug fix, janjf@cnr.colostate.edu */
               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 );
}


#ifdef _PARALLEL_H

/* set_term()
 *
 * set "term" variable.
 */

void set_term ( int t )
{
term = t;

return;
}


/* get_term()
 *
 * get "term" value.
 */

int get_term ( void )
{
return term;
}


/* send_generation_information()
 *
 * calculates and prints population statistics send by the different nodes.
 */

int send_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;
#ifdef _PARALLEL_H
     int low;
     int high;
#endif

     /* number of decimal digits to use when printing fitness values. */
     if ( fd == -1 )
          fd = atoi ( get_parameter ( "output.digits" ) );

#ifdef PDEBUG_GP
printf("Begin generation_info\n"); fflush(stdout);
#endif

     /* 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... */
    low = get_low();
     high = get_high();
     for ( i = low; i < high; ++i )
     {
#ifdef PDEBUG_GP
printf("gne_info = %d\n", i); fflush(stdout);
#endif
	  /* calculate stats for subpopulation. */
          calculate_pop_stats ( gen_stats+i+1, mpop->pop[i], gen, i );

	  /* send stats for the i-th subpopulation. */
          send_pop_stats ( gen_stats+i+1, 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;
     }

    /* merge stats for current generation into overall run stats. */
     newbest = accumulate_pop_stats ( run_stats, gen_stats );

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

     /* free stats structures for current generation. */
     low = get_low();
     high = get_high();

     /* We used only a part of the stats */
     for ( i =  low+1; i < high+1; ++i )
     {
          for ( j = 0; j < gen_stats[i].bestn; ++j )
               --gen_stats[i].best[j]->refcount;
          FREE ( gen_stats[i].best );
     }

     /* Last but not least */
     for ( j = 0; j < gen_stats[0].bestn; ++j )
          --gen_stats[0].best[j]->refcount;

     FREE ( gen_stats[0].best );
     FREE ( gen_stats );

     /* deallocate saved individuals that are no longer needed. */
     saved_individual_gc();

#ifdef PDEBUG_GP
printf("Finished generation_info()\n"); fflush(stdout);
#endif

     /* return value the application callback gave us. */
     return ret;

}


/* pack_saved_ind()
 *
 * packs a saved individual.
 */

void pack_saved_ind (saved_ind *ptr)

{
pack_individual(ptr->ind);
if (pvm_pkint (&(ptr->refcount), 1, 1))
	pvm_perror("pack_saved_ind error packing \"refcount\"");

return;
}
 

/* unpack_saved_ind()
 *
 * unpacks a saved individual.
 */

void unpack_saved_ind (saved_ind **ptr)
{
saved_ind *ret;

ret = (saved_ind *)MALLOC ( sizeof ( saved_ind ));

unpack_individual(&(ret->ind));

if (pvm_upkint (&(ret->refcount), 1, 1))
	pvm_perror("unpack_saved_ind error unpacking \"refcount\"");

*ptr = ret;

return;
}


/* send_pop_stats()
 *
 * send the statistics to the master node.
 */

void send_pop_stats ( popstats *s, int gen, int subpop )
{
int i;
int parent_tid;

parent_tid = pvm_parent();

if (parent_tid == PvmNoParent)
	{
	error( E_FATAL_ERROR, "No parent for process in send_pop_stats()");
	}

pvm_initsend(ENCODING);

if (pvm_pkint(&gen, 1, 1))
	pvm_perror("send_pop_stats error packing \"gen\"");
if (pvm_pkint(&subpop, 1, 1))
	pvm_perror("send_pop_stats error packing \"subpop\"");
if (pvm_pkint(&s->real_size, 1, 1))
	pvm_perror("send_pop_stats error packing \"real_size\"");

/* Pack the original information elements */

if (pvm_pkint(&s->size, 1, 1))
	pvm_perror("send_pop_stats error packing \"size\"");
if (pvm_pkint(&s->maxnodes, 1, 1))
	pvm_perror("send_pop_stats error packing \"maxnodes\"");
if (pvm_pkint(&s->minnodes, 1, 1))
	pvm_perror("send_pop_stats error packing \"minnodes\"");
if (pvm_pkint(&s->totalnodes, 1, 1))
	pvm_perror("send_pop_stats error packing \"totalnodes\"");
if (pvm_pkint(&s->bestnodes, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestnodes\"");
if (pvm_pkint(&s->worstnodes, 1, 1))
	pvm_perror("send_pop_stats error packing \"worstnodes\"");

if (pvm_pkint(&s->maxdepth, 1, 1))
	pvm_perror("send_pop_stats error packing \"maxdepth\"");
if (pvm_pkint(&s->mindepth, 1, 1))
	pvm_perror("send_pop_stats error packing \"mindepth\"");
if (pvm_pkint(&s->totaldepth, 1, 1))
	pvm_perror("send_pop_stats error packing \"totaldepth\"");
if (pvm_pkint(&s->bestdepth, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestdepth\"");
if (pvm_pkint(&s->worstdepth, 1, 1))
	pvm_perror("send_pop_stats error packing \"worstdepth\"");

if (pvm_pkint(&s->maxhits, 1, 1))
	pvm_perror("send_pop_stats error packing \"maxhits\"");
if (pvm_pkint(&s->minhits, 1, 1))
	pvm_perror("send_pop_stats error packing \"minhits\"");
if (pvm_pkint(&s->totalhits, 1, 1))
	pvm_perror("send_pop_stats error packing \"totalhits\"");
if (pvm_pkint(&s->besthits, 1, 1))
	pvm_perror("send_pop_stats error packing \"besthits\"");
if (pvm_pkint(&s->worsthits, 1, 1))
	pvm_perror("send_pop_stats error packing \"worsthits\"");

if (pvm_pkdouble(&s->bestfit, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestfit\"");
if (pvm_pkdouble(&s->worstfit, 1, 1))
	pvm_perror("send_pop_stats error packing \"worstfit\"");
if (pvm_pkdouble(&s->totalfit, 1, 1))
	pvm_perror("send_pop_stats error packing \"totalfit\"");

if (pvm_pkint(&s->bestgen, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestgen\"");
if (pvm_pkint(&s->worstgen, 1, 1))
	pvm_perror("send_pop_stats error packing \"worstgen\"");

if (pvm_pkint(&s->bestpop, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestpop\"");
if (pvm_pkint(&s->worstpop, 1, 1))
	pvm_perror("send_pop_stats error packing \"worstpop\"");

if (pvm_pkint(&s->bestn, 1, 1))
	pvm_perror("send_pop_stats error packing \"bestn\"");

/* Pack the different individuals */
for (i=0; i<s->real_size; i++)
	pack_saved_ind (s->best[i]);

/* Pack some timing information */
pack_event(get_tree_send_event());
pack_event(get_tree_receive_event());
pack_event(get_individual_send_event());
pack_event(get_individual_receive_event());
// use global vsriable :-( should be removed later...
pack_event(&eval_diff);
pack_event(&breed_diff);


/* Send it */
pvm_send(parent_tid, STATS_TAG);

/*
printf ("Trees RX %s TX %s -- ", event_string(get_tree_receive_event())
			       , event_string(get_tree_send_event()));
printf ("Individuals RX %s TX %s\n", event_string(get_individual_receive_event())
				   , event_string(get_individual_send_event()));
*/


return;
}


/* receive_pop_stats()
 *
 * receives the statistics from one the slave nodes, allocates the necessary memory.  
 */

void receive_pop_stats ( popstats *s, int *gen, int *subpop )
{
int i, j;
saved_ind *shp;

/* Unpack the information needed place the statistics in
   the correct context.
*/
if (pvm_upkint(gen, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"gen\"");
if (pvm_upkint(subpop, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"subpop\"");

/* !! We shift the pointer to the statistics HERE !! */
s += *subpop + 1;

if (pvm_upkint(&s->real_size, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"real_size\"");

/* Unpack the original information elements */
if (pvm_upkint(&s->size, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"size\"");
if (pvm_upkint(&s->maxnodes, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"maxnodes\"");
if (pvm_upkint(&s->minnodes, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"minnodes\"");
if (pvm_upkint(&s->totalnodes, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"totalnodes\"");
if (pvm_upkint(&s->bestnodes, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestnodes\"");
if (pvm_upkint(&s->worstnodes, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worstnodes\"");

if (pvm_upkint(&s->maxdepth, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"maxdepth\"");
if (pvm_upkint(&s->mindepth, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"mindepth\"");
if (pvm_upkint(&s->totaldepth, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"totaldepth\"");
if (pvm_upkint(&s->bestdepth, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestdepth\"");
if (pvm_upkint(&s->worstdepth, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worstdepth\"");

if (pvm_upkint(&s->maxhits, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"maxhits\"");
if (pvm_upkint(&s->minhits, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"minhits\"");
if (pvm_upkint(&s->totalhits, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"totalhits\"");
if (pvm_upkint(&s->besthits, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"besthits\"");
if (pvm_upkint(&s->worsthits, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worsthits\"");

if (pvm_upkdouble(&s->bestfit, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestfit\"");
if (pvm_upkdouble(&s->worstfit, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worstfit\"");
if (pvm_upkdouble(&s->totalfit, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"totalfit\"");

if (pvm_upkint(&s->bestgen, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestgen\"");
if (pvm_upkint(&s->worstgen, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worstgen\"");

if (pvm_upkint(&s->bestpop, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestpop\"");
if (pvm_upkint(&s->worstpop, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"worstpop\"");

if (pvm_upkint(&s->bestn, 1, 1))
	pvm_perror("receive_pop_stats error unpacking \"bestn\"");

/* allocate a list of the top N individuals. */
s->best = (saved_ind **)MALLOC ( s->bestn * sizeof ( saved_ind * ) );

/* We unpack the individuals, these are already the best ones */
for (i=0; i<s->real_size; i++)
	unpack_saved_ind (s->best+i);

/* The individuals should be put in the linked list of saved individual */
     /** now save copies of the individuals in the "temp" list **/
     for ( i = 0; i < s->real_size; ++i )
     {
	  shp = s->best[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;
     }

/* Unpack thtiming information */
unpack_event(get_tree_send_event());
unpack_event(get_tree_receive_event());
unpack_event(get_individual_send_event());
unpack_event(get_individual_receive_event());
unpack_event(&eval);
unpack_event(&breeding);

return;
}


/* receive_generation_information()
 *
 * receives and prints population statistics send by the different nodes.
 */

Popstats_list *receive_generation_information( multipop *mpop, int *gen, Popstats_list **stats_list )
{
     int i;
     int newbest;
     static int fd = -1;
     int ret = 0;
     int bestn = 0;
     int stt_interval = 0;
     int subpop;

     char keep_gen_stats = 0;

     popstats *gen_stats = NULL;
     Popstats_list *stats;

     FILE *bout, *hout;


     /* number of decimal digits to use when printing fitness values. */
     if ( fd == -1 )
          fd = atoi ( get_parameter ( "output.digits" ) );

     bestn = atoi ( get_parameter ( "output.bestn" ) );
     if ( bestn < 1 )
       {
       error ( E_WARNING, "\"output.bestn\" must be at least 1.  defaulting to 1." );
       bestn = 1;
       }

     /* 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." );

#ifdef PDEBUG_GP
printf("Begin receive_generation_info\n"); fflush(stdout);
#endif

     /* During the first call we need to allocate the memory for the run statistics. */
     if (run_stats == NULL)
	  {
	  /* 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].best = NULL;
	       	run_stats[i].size = -1;
		}
	  }

     /* During the first call we need to allocate the memory for the first element of the list. */
     if (saved_head == NULL)
	  {
	  /* 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;
	  }

     /* allocate stats records to receive the statistics. */
     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].best = NULL;
          gen_stats[i].size = -1;
     	  }

     /* receive stats for exactly "1" subpopulation. The statistics
	are immediatly stored in the correct place in the array. (gen_stats) */
     receive_pop_stats ( gen_stats, gen, &subpop );

     /* I am sooo lazy ... */
     i = subpop;

     /* Look of statistics in the list */
     stats = find_popstats ( stats_list, *gen);

#ifdef PDEBUG_GP
printf ("stats->gen = %d , stats->count = %d , gen = %d\n", stats->gen, stats->count, *gen);
#endif

     /* Did we already have this one in stock? ;) */
     if (stats->count == 0 )
	{
	/* No, so we take use the currently allocated popstats (gen_stats)
	   We should also make sure that we do not deallocate the memory now! */
	stats->stats = gen_stats;
	stats->gen = *gen;

	keep_gen_stats = 1;
	}
     else
	{
	/* We already had some statistics about this population */

	/* we copy the new stats in it. */
	stats->stats[i+1] =  gen_stats[i+1];

	/* We deallocate the statistics : without dereferencing the individuals! */
	FREE(gen_stats);

	/* We substitute the statistics we already have to gen_stats */
	gen_stats = stats->stats;
	}

     stats->count++;
     stats->eval[i] = eval;
     stats->breeding[i] = breeding;

     oprintf ( OUT_GEN, 90, "=== GENERATION %d ===\n", *gen );
     oprintf ( OUT_PRG, 90, "=== GENERATION %d ===\n", *gen );

     /* for each subpopulation... */
     {
#ifdef PDEBUG_GP
printf("receive_generation_info subpop = %d\n", i); fflush(stdout);
#endif
	  /* 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 )
               break;
*/
	  /** 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 %d ", *gen, i+1 );
               oprintf ( OUT_STT, 50, "%.*lf %.*lf %.*lf ",
                        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 %.3lf %d %d %d %d ",
                        (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 %.*lf %.*lf ",
                        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 %.3lf %d %d %d %d ",
                        (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" );
          }
            
     }

     if (stats->count == mpop->size)
	{
     	/* 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 0 ", *gen );
               oprintf ( OUT_STT, 50, "%.*lf %.*lf %.*lf ",
                        fd, gen_stats[0].totalfit/gen_stats[0].size,
                        fd, gen_stats[0].bestfit, fd, gen_stats[0].worstfit );
               oprintf ( OUT_STT, 50, "%.3lf %.3lf %d %d %d %d ",
                        (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 %.*lf %.*lf ",
                        fd, run_stats[0].totalfit/run_stats[0].size,
                        fd, run_stats[0].bestfit, fd, run_stats[0].worstfit );
               oprintf ( OUT_STT, 50, "%.3lf %.3lf %d %d %d %d ",
                        (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 ( 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 );

     } /* IF COUNT == mpop->size */



#ifdef PDEBUG_GP
printf("Finished receive_generation_info()\n"); fflush(stdout);
#endif

if (stats->count == mpop->size)
	return stats;

return NULL;
}


/* free_gen_stats()
 *
 * frees the memory referred to by gen_stats
 */

void free_gen_stats (multipop *mpop)
{
int i, j;


     	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 );


/* deallocate saved individuals that are no longer needed. */
saved_individual_gc();

}


/* get_current_generation()
 *
 * returns the generation being processed on this node
 */

int get_current_generation ( void )
{
return gen;
}


/* alloc_popstats_list()
 *
 * adds a link in front of the list given as parameter, NO ALLOCATION !!!
 */

Popstats_list *alloc_popstats_list ( Popstats_list *p )
{
char *parameter;
Popstats_list *ret;
event *e;
int number_of_populations;
int i;

parameter = get_parameter("multiple.subpops");

if (parameter == NULL)
	error (E_FATAL_ERROR, "get_parameter() failed in alloc_popstats_list()");

number_of_populations = atoi (parameter);

ret = (Popstats_list *) MALLOC (sizeof(Popstats_list));

if (ret == NULL)
	error (E_FATAL_ERROR, "Memory allocation failed in alloc_popstats_list() I");

e = (event *) MALLOC (sizeof(event) * number_of_populations);

if (e == NULL)
	error (E_FATAL_ERROR, "Memory allocation failed in alloc_popstats_list() II");

/* Set some illegal values in the array */
for (i=0; i<number_of_populations; i++)
	e[i].wall = -1;

ret->eval = e;

e = (event *) MALLOC (sizeof(event) * number_of_populations);

if (e == NULL)
	error (E_FATAL_ERROR, "Memory allocation failed in alloc_popstats_list() III");

/* Set some illegal values in the array */
for (i=0; i<number_of_populations; i++)
	e[i].wall = -1;

ret->breeding = e;

ret->gen = -9999;
ret->count = 0;
ret->next = p;

return ret;
}


/* dealloc_popstats_list()
 *
 * frees the linked list of population statistics
 */

void dealloc_popstats_list ( Popstats_list *p, multipop *mpop )
{
int i, j;
int counter = 0;

Popstats_list *tmp;
popstats *gen_stats;

printf ("Deallocating list of statistics ");


while ( p != NULL )
	{
	counter++;
	tmp = (Popstats_list *) p->next;

	gen_stats = p->stats;

	/* Free the statistics */
	for (i=0; i<mpop->size+1; i++)
		{
		for ( j = 0; j < gen_stats[i].bestn; ++j )
			if ( gen_stats[i].best )
			--gen_stats[i].best[j]->refcount;

		FREE ( gen_stats[i].best );
		}

	FREE(p->stats);
	FREE(p->eval);
	FREE(p->breeding);

	/* Garbage collect the individuals who are not referenced anymore */
	saved_individual_gc();

	/* Free this link */
	FREE(p);

	/* Move to the next link */
	p = tmp;

	printf (".");
	fflush(stdout);
	}

printf (" %d items removed.\n", counter);
	
return;
}


/* find_popstats()
 *
 * find the population statistics for the given generation. If these are not found an additional link
 * is allocated.
 */

Popstats_list *find_popstats ( Popstats_list **p, int gen )
{
Popstats_list *tmp;

/*
tmp = *p;

while  ( tmp != NULL )
	{
	printf ("gen%d #%d ", tmp->gen, tmp->count);
	fflush(stdout);

	tmp = (Popstats_list *) tmp->next;
	}
printf("\n");
*/

tmp = *p;

while  ( (tmp != NULL) && (tmp->gen != gen) )
	{
	tmp = (Popstats_list *) tmp->next;
	}

/* Not found in the list so we put a new link in front of the list */
if ( tmp == NULL )
	{
	tmp = *p
	    = alloc_popstats_list(*p);
	}

return tmp;
}


/* remove_statistics()
 *
 * removes a link from the linked list
 */

Popstats_list *remove_statistics( Popstats_list *l, Popstats_list *p, multipop *mpop )
{
int i, j;
popstats *gen_stats;
Popstats_list *tmp;

tmp = l;

/* Find the link in the list */
while  ( (tmp != NULL) && (tmp->next != p) )
	tmp = (Popstats_list *) tmp->next;


/* If we found one we delete the information it refers to and reomve if from the list */
if (tmp != NULL)
	{
	/* We found it */
	/* Change the link */
	tmp->next = p->next;
/*
printf ("Removing stats for generation %d\n", p->gen);
*/
	gen_stats = p->stats;

	/* Free the statistics */
	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(p->stats);
	FREE(p->eval);
	FREE(p->breeding);

	/* Garbage collect the individuals who are not referenced anymore */
	saved_individual_gc();

	/* Free this link */
	FREE(p);
	}

/* Maybe this was the head of the list */
if (l == p)
	l = tmp;

return l;
}

#endif