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

/* initialize_topology()
 *
 * reads the parameter database and builds the exchange table.
 */

void initialize_topology ( multipop *mpop )
{
     char pnamebuf[100], pnamebuf2[100];
     char *param, *param2, *param3;
     int i, j, k;
     int errors = 0;

     if ( mpop->size == 1 )
     {
	  /* singlepop problem -- no topology needed. */
          mpop->exch = NULL;
          mpop->exchanges = -1;
          return;
     }

     oprintf ( OUT_SYS, 30, "building subpopulation exchange topology:\n" );
     
     param = get_parameter ( "multiple.exchanges" );
     if ( param == NULL )
     {
	  /* multipop problem, but no exchanges specified. */
	  
          mpop->exch = NULL;
          mpop->exchanges = 0;
          return;
     }
     else
     {
          mpop->exchanges = atoi ( param );
          if ( mpop->exchanges < 0 )
               error ( E_FATAL_ERROR, "\"exchanges\" must be nonnegative." );
     }

     mpop->exch = (exchange *)MALLOC ( mpop->exchanges * sizeof ( exchange ) );
     
     for ( i = 0; i < mpop->exchanges; ++i )
     {
	  /** read the destination subpop. **/
	  
          sprintf ( pnamebuf, "exch[%d].to", i+1 );
          param = get_parameter ( pnamebuf );
          if ( param == NULL )
          {
               ++errors;
               error ( E_ERROR, "\"%s\" must be set.", pnamebuf );
          }
          else
          {
               mpop->exch[i].to = atoi ( param ) - 1;
               if ( mpop->exch[i].to < 0 || mpop->exch[i].to >= mpop->size )
               {
                    ++errors;
                    error ( E_ERROR, "\"%s\" is out of range.\n", pnamebuf );
               }
          }

	  /** read how the individuals to be replaced in the destination
	    subpop are selected. **/
	  
          sprintf ( pnamebuf, "exch[%d].toselect", i+1 );
          mpop->exch[i].tosc = get_parameter ( pnamebuf );
          if ( mpop->exch[i].tosc == NULL )
          {
               ++errors;
               error ( E_ERROR, "\"%s\" must be set.", pnamebuf );
          }
          else
          {
               if ( ! exists_select_method ( mpop->exch[i].tosc ) )
               {
                    ++errors;
                    error ( E_ERROR, "\"%s\": \"%s\" is not a selection method.",
                           pnamebuf, mpop->exch[i].tosc );
               }
          }

	  /** read how many individuals are to be exchanged in this
	    manner. **/
	  
          sprintf ( pnamebuf, "exch[%d].count", i+1 );
          param = get_parameter ( pnamebuf );
          if ( param == NULL )
          {
               ++errors;
               error ( E_ERROR, "\"%s\" must be set.", pnamebuf );
          }
          else
          {
               mpop->exch[i].count = atoi ( param );
               if ( mpop->exch[i].count < 0 )
               {
                    ++errors;
                    error ( E_ERROR, "\"%s\" must be nonnegative.", pnamebuf );
               }
          }

	  /** check to see if "from" is specified without a "tree[#]". **/
	  
          sprintf ( pnamebuf, "exch[%d].from", i+1 );
          param = get_parameter ( pnamebuf );
          if ( param )
          {
               /** if "from" is specified, then we're copying whole individuals
		 from one subpop to another. **/

	       /* these arrays are not needed. */
               mpop->exch[i].from = NULL;
               mpop->exch[i].as = NULL;
	       /* allocate an array of one string (to hold the selection
		  method). */
               mpop->exch[i].fromsc = (char **)MALLOC ( sizeof ( char * ) );

	       /* the subpop that individuals are taken from. */
               mpop->exch[i].copywhole = atoi ( param ) - 1;
               if ( mpop->exch[i].copywhole < 0 ||
                    mpop->exch[i].copywhole >= mpop->size )
               {
                    ++errors;
                    error ( E_ERROR, "\"%s\" is out of range.", pnamebuf );
               }

	       /* the selection method used to pick the individuals from the
		  source subpop. */
               sprintf ( pnamebuf, "exch[%d].fromselect", i+1 );
               mpop->exch[i].fromsc[0] = get_parameter ( pnamebuf );
               if ( mpop->exch[i].fromsc[0] == NULL )
               {
                    ++errors;
                    error ( E_ERROR, "\"%s\" must be set.", pnamebuf );
               }
               else
               {
                    if ( ! exists_select_method ( mpop->exch[i].fromsc[0] ) )
                    {
                         ++errors;
                         error ( E_ERROR, "\"%s\": \"%s\" is not a selection method.",
                                pnamebuf, mpop->exch[i].fromsc[0] );
                    }
               }
          }
          else
          {

               /** since "from" is not defined, we're taking trees from different
		 subpops and merging them to create a composite individual to place
		 in the destination subpop. **/
	       
               mpop->exch[i].copywhole = -1;
	       /* this array lists, for each tree, which subpop it comes from. */
               mpop->exch[i].from = (int *)MALLOC ( tree_count * sizeof ( int ) );
	       /* this array keeps track of when two trees are supposed to always
		  come from the same individual (not just the same subpop). */
               mpop->exch[i].as = (int *)MALLOC ( tree_count * sizeof ( int ) );
	       /* this array holds the selection method strings used for each
		  tree. */
               mpop->exch[i].fromsc = (char **)MALLOC ( tree_count * sizeof ( char * ) );

	       /* get the default selection method, if one is specified. */
               sprintf ( pnamebuf, "exch[%d].fromselect", i+1 );
               param3 = get_parameter ( pnamebuf );
               
               for ( j = 0; j < tree_count; ++j )
               {
		    /** for each tree, attempt to read the "from" and
		      "fromselect" parameters. **/
		    
                    sprintf ( pnamebuf, "exch[%d].from.tree[%d]", i+1, j );
                    param = get_parameter ( pnamebuf );
                    sprintf ( pnamebuf2, "exch[%d].fromselect.tree[%d]",
                             i+1, j );
                    param2 = get_parameter ( pnamebuf2 );

                    if ( param == NULL && param2 == NULL )
                    {
			 /* neither is set, we're supposed to leave this
			    tree untouched in the destination individual. */
			 
                         mpop->exch[i].from[j] = -1;
                         mpop->exch[i].as[j] = -1;
                         mpop->exch[i].fromsc[j] = NULL;
                    }
                    else if ( param2 == NULL )
                    {
                         /* only "from" is set, examine param3 for default
			    selection method. */

			 /* source subpop. */
                         mpop->exch[i].from[j] = atoi ( param ) - 1;
                         if ( mpop->exch[i].from[j] < 0 || mpop->exch[i].from[j] >= mpop->size )
                         {
                              ++errors;
                              error ( E_ERROR, "\"%s\" is out of range.", pnamebuf );
                         }

			 /* no default set, error. */
                         if ( param3 == NULL )
                         {
                              ++errors;
                              error ( E_ERROR, "\"%s\" must be set.", pnamebuf2 );
                         }
                         else
                         {
                              mpop->exch[i].as[j] = -1;
                              if ( ! exists_select_method ( param3 ) )
                              {
                                   ++errors;
                                   error ( E_ERROR, "\"%s\": \"%s\" is not a selection method.",
                                          pnamebuf, param3 );
                              }
                         }
                         mpop->exch[i].fromsc[j] = param3;
                    }
                    else if ( param == NULL )
                    {
                         /* only "fromselect" is set; it better be of the form
			    "as_#". */
                         
                         if ( strncmp ( param2, "as_", 3 ) == 0 )
                         {
                              mpop->exch[i].from[j] = -1;
                              mpop->exch[i].fromsc[j] = NULL;
			      /* "as" stores which tree this one comes from the
				 same subpop as. */
                              mpop->exch[i].as[j] = atoi ( param2 + 3 );
                              if ( mpop->exch[i].as[j] < 0 ||
                                  mpop->exch[i].as[j] >= tree_count )
                              {
                                   ++errors;
                                   error ( E_ERROR, "\"%s\" is out of range.", pnamebuf2 );
                              }
                         }
                         else
                         {
                              ++errors;
                              error ( E_ERROR, "\"%s\" must be \"as_#\".", pnamebuf2 );
                         }
                    }
                    else
                    {
                         /* they're both set. */

                         mpop->exch[i].as[j] = -1;
                         mpop->exch[i].from[j] = atoi ( param ) - 1;
                         if ( mpop->exch[i].from[j] < 0 || mpop->exch[i].from[j] >= mpop->size )
                         {
                              ++errors;
                              error ( E_ERROR, "\"%s\" is out of range.", pnamebuf );
                         }
                         mpop->exch[i].fromsc[j] = param2;
                         if ( ! exists_select_method ( param2 ) )
                         {
                              ++errors;
                              error ( E_ERROR, "\"%s\": \"%s\" is not a selection method.",
                                     pnamebuf2, param2 );
                         }
                    }
               }

	       /* now we need to resolve any chains of "as_" references: if
		  tree 2 comes from the same individual as tree 1, and tree 1
		  comes from the same individual as tree 0, we need to change that
		  to say that both 2 and 1 come from tree 0.

		  also detect circular references. */
	       
               for ( j = 0; j < tree_count; ++j )
               {
                    if ( mpop->exch[i].as[j] == -1 )
                         continue;
                    k = mpop->exch[i].as[j];
                    while ( k != -1 )
                    {
                         if ( k == j )
                         {
                              ++errors;
                              error ( E_ERROR, "Circular reference resolving \"exch[%d].fromselect.tree[%d]\".",
                                     i+1, j );
                              j = tree_count;
                              break;
                         }
                         mpop->exch[i].as[j] = k;
                         k = mpop->exch[i].as[k];
                    }
                    k = mpop->exch[i].as[j];
                    if ( mpop->exch[i].from[k] == -1 && mpop->exch[i].as[k] == -1 )
                         mpop->exch[i].as[j] = -1;
               }
          }

          
#ifdef DEBUG
	  /* print out information on this exchange. */
          printf ( "exchange %d:\n", i+1 );
          printf ( "to: %d; count: %d; select: %s\n", mpop->exch[i].to,
                  mpop->exch[i].count, mpop->exch[i].tosc );
          if ( mpop->exch[i].copywhole == -1 )
          {
               for ( j = 0; j < tree_count; ++j )
               {
                    param = mpop->exch[i].fromsc[j];
                    printf ( "   %3d:  from: %3d   as: %3d   select: %s\n",
                            j, mpop->exch[i].from[j], mpop->exch[i].as[j],
                            param==NULL?"NULL":param );
               }
          }
          else
          {
               param = mpop->exch[i].fromsc[0];
               printf ( "copywhole: %d   select: %s\n",
                       mpop->exch[i].copywhole, param==NULL?"NULL":param );
          }
#endif
     }

     /* if any errors occurred then stop now. */
     if ( errors )
          error ( E_FATAL_ERROR, "Errors occurred while building topology.  Aborting." );

     /* print out the summary of exchanges. */
     oprintf ( OUT_SYS, 30, "    %d exchange(s) total.\n", mpop->exchanges );
     for ( i = 0; i < mpop->exchanges; ++i )
     {
          oprintf ( OUT_SYS, 30, "    exchange %d:\n", i+1 );
          oprintf ( OUT_SYS, 30, "        replace %d individual(s) in subpop %d (selected by %s)\n",
                   mpop->exch[i].count, mpop->exch[i].to+1, mpop->exch[i].tosc );
          if ( mpop->exch[i].copywhole != -1 )
               oprintf ( OUT_SYS, 30, "        with individual(s) from subpop %d (selected by %s)\n",
                        mpop->exch[i].copywhole+1, mpop->exch[i].fromsc[0] );
          else
               for ( j = 0; j < tree_count; ++j )
               {
                    if ( mpop->exch[i].from[j] == -1 )
                    {
                         if ( mpop->exch[i].as[j] == -1 )
                              oprintf ( OUT_SYS, 30, "        tree %d: leaving original tree\n", j );
                         else
                              oprintf ( OUT_SYS, 30, "        tree %d: from same individual as tree %d\n", j, mpop->exch[i].as[j] );
                    }
                    else
                         oprintf ( OUT_SYS, 30, "        tree %d: from subpop %d (selected by %s)\n", j,
                                  mpop->exch[i].from[j]+1, mpop->exch[i].fromsc[j] );
               }
     }

}

/* free_topology()
 *
 * this frees the topology table.
 */

void free_topology ( multipop *mpop )
{
     int i;
     for ( i = 0; i < mpop->exchanges; ++i )
     {
          if ( mpop->exch[i].from )
               FREE ( mpop->exch[i].from );
          if ( mpop->exch[i].as )
               FREE ( mpop->exch[i].as );
          FREE ( mpop->exch[i].fromsc );
     }
     if ( mpop->exch )
          FREE ( mpop->exch );
}

/* exchange_subpopulations()
 *
 * this performs the actual exchanges, using the information stored
 * in the exchange table.
 */

void exchange_subpopulations ( multipop *mpop )
{
     int i, j, k;
     sel_context *tocon;
     sel_context **fromcon;
     select_context_func_ptr select_con;
     int tp, *fp;
     int ti, *fi;

#ifdef _PARALLEL_H
     /* Are we working in parallel  */
     if (get_operation_mode() == SLAVE)
	{
	exchange_filter(mpop);

	return;
	}
#endif

     /** arrays used for composite individuals. **/

     /* fromcon[j] holds the selection context used to pick individual
	to take tree j from. */
     fromcon = (sel_context **)MALLOC ( tree_count * sizeof ( sel_context * ) );
     /* fp[j] holds the population from which to take tree j from. */
     fp = (int *)MALLOC ( tree_count * sizeof ( int ) );
     /* fi[j] holds the individual from which to take tree j from. */
     fi = (int *)MALLOC ( tree_count * sizeof ( int ) );

     for ( i = 0; i < mpop->exchanges; ++i )
     {
	  /* where individuals are going. */
          tp = mpop->exch[i].to;

	  /* set up selection method to pick individuals to be replaced. */
          select_con = get_select_context ( mpop->exch[i].tosc );
          tocon = select_con ( SELECT_INIT, NULL, mpop->pop[tp],
                              mpop->exch[i].tosc );

	  /* are we copying whole individuals or creating composites? */
          if ( mpop->exch[i].copywhole > -1 )
          {
	       /*** copying whole individuals. ***/

	       /* the source subpop. */
               fp[0] = mpop->exch[i].copywhole;

	       /* selection method for choosing individuals from source
		  subpop. */
               select_con = get_select_context ( mpop->exch[i].fromsc[0] );
               fromcon[0] = select_con ( SELECT_INIT, NULL, mpop->pop[fp[0]],
                                        mpop->exch[i].fromsc[0] );

               for ( k = 0; k < mpop->exch[i].count; ++k )
               {
		    /** pick an individual to be replaced that has not already
		      been replaced during this exchange cycle. **/
                    do
                    {
                         ti = tocon->select_method ( tocon );
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );

		    /* pick an individual from the source subpop. */
                    fi[0] = fromcon[0]->select_method ( fromcon[0] );
                                        
                    /** remove the old iondividual from the population. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* always dereference ERCs when removing trees
			    from the population. */
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, -1 );
                         free_tree ( mpop->pop[tp]->ind[ti].tr+j );
                    }

		    /* copy the individual. */
                    duplicate_individual ( mpop->pop[tp]->ind+ti,
                                           mpop->pop[fp[0]]->ind+fi[0] );

		    /* reference the ERCs in the new individual. */
                    for ( j = 0; j < tree_count; ++j )
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, 1 );

		    /* mark the individual as just coming from an exchange. */
                    mpop->pop[tp]->ind[ti].flags = FLAG_NEWEXCH;
               }

	       /* all done with this exchange, delete the selection context. */
               fromcon[0]->context_method ( SELECT_CLEAN, fromcon[0],
                                           NULL, NULL );
          }
          else
          {
               /*** creating composite individuals. ***/

	       /** create selection contexts for each tree. **/
               for ( j = 0; j < tree_count; ++j )
               {
		    /* does this tree need a context? */
                    if ( mpop->exch[i].fromsc[j] )
                    {
			 /* create it. */
                         select_con = get_select_context ( mpop->exch[i].fromsc[j] );
                         fromcon[j] = select_con ( SELECT_INIT, NULL,
                                                  mpop->pop[mpop->exch[i].from[j]],
                                                  mpop->exch[i].fromsc[j] );
                    }
                    else
			 /* don't need one. */
                         fromcon[j] = NULL;
               }

               for ( k = 0; k < mpop->exch[i].count; ++k )
               {
		    /** select an individual to be replaced that hasn't already
		      been during this exchange cycle. **/
                    do
                    {
                         ti = tocon->select_method ( tocon );
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );
                    
		    /** now select the individuals that we will merge to
		      replace trees of the destination individual. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* we don't need to do a selection for a particular
			    tree if (1) it uses the same individual as another
			    tree or (2) it doesn't get replaced in the destination
			    individual. */

                         fp[j] = mpop->exch[i].from[j];
                         if ( fp[j] != -1 )
                         {
                              fi[j] = fromcon[fp[j]]->select_method ( fromcon[fp[j]] );
                         }
                    }

		    /** now resolve "as_" references in the fp and fi arrays. */
                    for ( j = 0; j < tree_count; ++j )
                         if ( fp[j] == -1 )
                         {
                              if ( mpop->exch[i].as[j] == -1 )
				   /* tree j doesn't get replaced, so set both
				      values to -1. */
                                   fp[j] = fi[j] = -1;
                              else
                              {
				   /* tree j comes from the same individual as
				      some other tree. */
                                   fp[j] = fp[mpop->exch[i].as[j]];
                                   fi[j] = fi[mpop->exch[i].as[j]];
                              }
                         }

                    /** replace the appropriate parts of the old tree. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* skip trees that don't get replaced. */
                         if ( fp[j] == -1 )
                              continue;

			 /* dereference ERCs in old tree. */
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, -1 );
			 /* delete old tree. */
                         free_tree ( mpop->pop[tp]->ind[ti].tr+j );
			 /* copy new tree. */
                         copy_tree ( mpop->pop[tp]->ind[ti].tr+j, mpop->pop[fp[j]]->ind[fi[j]].tr+j );
			 /* reference ERCs in new tree. */
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, 1 );
                    }
		    /* evaluate the fitness of the new composite individual. */
                    app_eval_fitness ( mpop->pop[tp]->ind+ti );
                    mpop->pop[tp]->ind[ti].flags = FLAG_NEWEXCH;
               }

	       /* destroy source selection contexts. */
               for ( j = 0; j < tree_count; ++j )
                    if ( fromcon[j] )
                         fromcon[j]->context_method ( SELECT_CLEAN,
                                                     fromcon[j], NULL, NULL );
          }

	  /* destroy destination selection context. */
          tocon->context_method ( SELECT_CLEAN, tocon, NULL, NULL );
     }

     FREE ( fromcon );
     FREE ( fp );
     FREE ( fi );

     /* erase all the NEWEXCH flags. */
     for ( i = 0; i < mpop->size; ++i )
          for ( j = 0; j < mpop->pop[i]->size; ++j )
               mpop->pop[i]->ind[j].flags &= ~FLAG_NEWEXCH;
     
}
                   
/* rebuild_exchange_topology()
 *
 * rebuilds the exchange table.  called from user code after making changes
 * to the parameters governing exchanges.
 */

void rebuild_exchange_topology ( multipop *mpop )
{
     free_topology ( mpop );
     initialize_topology ( mpop );
}


#ifdef _PARALLEL_H

/* exchange_test()
 *
 * returns the exchange type: NO, SEND, RECEIVE, NORMAL
 */

int exchange_test( int tp, int fp )
{
     const int exch_matrix[2][2] = { { NOP, SEND }, { RECEIVE, NORMAL } };
     int from;
     int to;
     int low;
     int high;


     low = get_low();
     high = get_high();

     to = OTHER;
     if ( (low <= tp) && (tp < high) )
	{
	/* The destination population resides on this node */
	to = MYSELF;
	}

     from = OTHER;
     if ( (low <= fp) && (fp < high) )
	{
	/* The source population resides on this node */
	from = MYSELF;
	}
     

return exch_matrix[to][from];
}

/* exchange_filter()
 *
 * does a traversal of all the exchanges and invoke the appropriate functions
 */

void exchange_filter ( multipop *mpop )
{

     int i, j, k;
     sel_context **fromcon;
     select_context_func_ptr select_con;
     int tp, *fp;
     int *fi;
     int exch_type;
     int low;
     int high;


     low = get_low();
     high = get_high();

     /** arrays used for composite individuals. **/

     /* fromcon[j] holds the selection context used to pick individual
	to take tree j from. */
     fromcon = (sel_context **)MALLOC ( tree_count * sizeof ( sel_context * ) );
     /* fp[j] holds the population from which to take tree j from. */
     fp = (int *)MALLOC ( tree_count * sizeof ( int ) );
     /* fi[j] holds the individual from which to take tree j from. */
     fi = (int *)MALLOC ( tree_count * sizeof ( int ) );


     for ( i = 0; i < mpop->exchanges; ++i )
     {

	  /* where individuals are going. */
          tp = mpop->exch[i].to;


	  /* are we copying whole individuals or creating composites? */
          if ( mpop->exch[i].copywhole > -1 )
          {
	       /*** copying whole individuals. ***/

	       /* the source subpop. */
               fp[0] = mpop->exch[i].copywhole;

	       exch_type = exchange_test(tp, fp[0]);
	       if ( (exch_type == NOP) || (exch_type == RECEIVE))
		   continue;

	       /* selection method for choosing individuals from source
		  subpop. */
	       select_con = get_select_context ( mpop->exch[i].fromsc[0] );
	       fromcon[0] = select_con ( SELECT_INIT, NULL, mpop->pop[fp[0]],
                            mpop->exch[i].fromsc[0] );


               for ( k = 0; k < mpop->exch[i].count; ++k )
               {
		    /* pick an individual from the source subpop. */
                    fi[0] = fromcon[0]->select_method ( fromcon[0] );
                    
	    	    /* We know the: populations,, ind */
		    exchange_dispatch(mpop, exch_type, i, tp, fp[0], fi[0], -1);		
               }

	       /* all done with this exchange, delete the selection context. */
               fromcon[0]->context_method ( SELECT_CLEAN, fromcon[0],
                                           NULL, NULL );
          }
          else
          {

               /*** creating composite individuals. ***/
               /** create selection contexts for each tree. **/

               for ( j = 0; j < tree_count; ++j )
               {

		    exch_type = exchange_test(tp, mpop->exch[i].from[j]);
		    if ( (exch_type == NOP) || (exch_type == RECEIVE))
			 continue;

                    /* does this tree need a context? */
                    if ( mpop->exch[i].fromsc[j] )
                    {
                         /* create it. */
                         select_con = get_select_context ( mpop->exch[i].fromsc[j] );
                         fromcon[j] = select_con ( SELECT_INIT, NULL,
                                                  mpop->pop[mpop->exch[i].from[j]],
                                                  mpop->exch[i].fromsc[j] );
                    }
                    else
                         /* don't need one. */
                         fromcon[j] = NULL;
               }

               for ( k = 0; k < mpop->exch[i].count; ++k )
               {
       		    /** now select the individuals that we will merge to
		      replace trees of the destination individual. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* we don't need to do a selection for a particular
			    tree if (1) it uses the same individual as another
			    tree or (2) it doesn't get replaced in the destination
			    individual. */

                         fp[j] = mpop->exch[i].from[j];

			 exch_type = exchange_test(tp, fp[j]);
			 if ( (exch_type == NOP) || (exch_type == RECEIVE))
			      continue;


                         if ( fp[j] != -1 )
                         {
                              fi[j] = fromcon[fp[j]]->select_method ( fromcon[fp[j]] );
                         }
                    }

		    /** now resolve "as_" references in the fp and fi arrays. */
                    for ( j = 0; j < tree_count; ++j )
                         if ( fp[j] == -1 )
                         {
                              if ( mpop->exch[i].as[j] == -1 )
				   /* tree j doesn't get replaced, so set both
				      values to -1. */
                                   fp[j] = fi[j] = -1;
                              else
                              {
				   /* tree j comes from the same individual as
				      some other tree. */
                                   fp[j] = fp[mpop->exch[i].as[j]];
                                   fi[j] = fi[mpop->exch[i].as[j]];
                              }
                         }

                    /** replace the appropriate parts of the old tree. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* skip trees that don't get replaced. */
                         if ( fp[j] == -1 )
                              continue;

			 exch_type = exchange_test(tp, fp[0]);
			 if ( (exch_type == NOP) || (exch_type == RECEIVE))
			      continue;

		         exchange_dispatch(mpop, exch_type, i, tp, fp[j], fi[j], j);		
                    }
/*
 Is hier OK voor lokale uitwisselingen maar niet voor export toestanden
		    evaluate the fitness of the new composite individual. 
                    app_eval_fitness ( mpop->pop[tp]->ind+ti );
                    mpop->pop[tp]->ind[ti].flags = FLAG_NEWEXCH;
*/
               }

	       /* destroy source selection contexts. */
               for ( j = 0; j < tree_count; ++j )
                    if ( fromcon[j] )
                         fromcon[j]->context_method ( SELECT_CLEAN,
                                                     fromcon[j], NULL, NULL );
          }
     }

     /* erase all the NEWEXCH flags. */
     for ( i = low; i < high; ++i )
          for ( j = 0; j < mpop->pop[i]->size; ++j )
               mpop->pop[i]->ind[j].flags &= ~FLAG_NEWEXCH;



     FREE ( fromcon );
     FREE ( fp );
     FREE ( fi );

     /* Are there any tree or individuals send from other populations? */
     exchange_import( mpop );

}


/* exchange_dispatch()
 *
 * invokes the appropriate functions based on source and destination
 */

void exchange_dispatch(multipop *mpop, int exch_type, int exch, int tp, int fp, int fi, int ft)
{
switch(exch_type)
	{
	case NOP:
			{
#ifdef PDEBUG
printf ("\t NOP \n");
#endif
			/* Nothing to do this happens on another node */
			break;
			}

	case SEND:
			{
#ifdef PDEBUG
printf ("\tEXPORTING TO %d\n", tp);
#endif
			/* Export a tree or and individual */
			exchange_export ( mpop, exch, tp, fp, fi, ft);

			break;
			}

	case RECEIVE:
			{
#ifdef PDEBUG
printf ("\t RECEIVE \n");
#endif
			/* Do nothing yet */
			break;
			}

	case NORMAL:
			{
#ifdef PDEBUG
printf ("\tNORMAL from %d to %d\n", fp, tp);
#endif
			/* Both and destination are on this node */
			exchange_normal( mpop, exch, tp, fp, fi, ft);

			break;
			}

	default:
			{
			error ( E_FATAL_ERROR, "Exchange number %d is not valid", exch);
					
			break;
			}
	}

     return;
}


/* exchange_normal
 *
 * should do the same as the initial exchange function
 */

void exchange_normal ( multipop *mpop, int exch, int tp, int fp, int fi, int ft )
{
     const int i =  exch;
     int j;
     sel_context *tocon;
     select_context_func_ptr select_con;
     int ti;

     {
	  /* set up selection method to pick individuals to be replaced. */
          select_con = get_select_context ( mpop->exch[i].tosc );
          tocon = select_con ( SELECT_INIT, NULL, mpop->pop[tp],
                              mpop->exch[i].tosc );

	  /* are we copying whole individuals or creating composites? */
          if ( mpop->exch[i].copywhole > -1 )
          {
	       /*** copying whole individuals. ***/

		    /** pick an individual to be replaced that has not already
		      been replaced during this exchange cycle. **/
                    do
                    {
                         ti = tocon->select_method ( tocon );
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );

                    /** remove the old iondividual from the population. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* always dereference ERCs when removing trees
			    from the population. */
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, -1 );
                         free_tree ( mpop->pop[tp]->ind[ti].tr+j );
                    }

		    /* copy the individual. */
                    duplicate_individual ( mpop->pop[tp]->ind+ti,
                                           mpop->pop[fp]->ind+fi );

		    /* reference the ERCs in the new individual. */
                    for ( j = 0; j < tree_count; ++j )
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, 1 );

		    /* mark the individual as just coming from an exchange. */
                    mpop->pop[tp]->ind[ti].flags = FLAG_NEWEXCH;
          }
          else
          {
               /*** creating composite individuals. ***/

               {
		    /** select an individual to be replaced that hasn't already
		      been during this exchange cycle. **/
                    do
                    {
                         ti = tocon->select_method ( tocon );
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );
                    

		    /* dereference ERCs in old tree. */
                    reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[ft].data, -1 );
		    /* delete old tree. */
                    free_tree ( mpop->pop[tp]->ind[ti].tr+ft );
		    /* copy new tree. */
                    copy_tree ( mpop->pop[tp]->ind[ti].tr+ft, mpop->pop[fp]->ind[fi].tr+ft );
		    /* reference ERCs in new tree. */
                    reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[ft].data, 1 );
               
               }

          }

	  /* destroy destination selection context. */
          tocon->context_method ( SELECT_CLEAN, tocon, NULL, NULL );
     }
}


/* exchange_export()
 *
 * exports individuals to population not present on this node
 */

void exchange_export ( multipop *mpop, int exch, int tp, int fp, int fi, int ft )
{
int destination_node;

/* Determine the node on which the population resides */
destination_node = population_mapping (tp);

if (destination_node < 0)
	error (E_FATAL_ERROR, "In exchange_export() no node for population number %d", tp);


     /* are we copying whole individuals or creating composites? */
     if ( mpop->exch[exch].copywhole > -1 )
     {
     /*** copying whole individuals. ***/
     /* send the individual: exchange nr, pop, ind, ind ptr */
     send_individual ( destination_node, exch, mpop->pop[fp]->ind+fi );

     }
     else
     {
     /* send the tree: exchange nr, pop, ind, tree, tree ptr */
     send_tree ( destination_node, exch, ft, mpop->pop[fp]->ind[fi].tr+ft );

     }

return;
}


/* exchange_import()
 *
 * checks whether there are trees/individuals can be imported and merged/copied
 * this function has a event driven character since its processing is based on the
 * received messages.
 */

void exchange_import ( multipop *mpop )
{

int i, j, k;
int count;
int tmp;
sel_context *tocon;
select_context_func_ptr select_con;
int ti;
int ft;

int bufid;
int exch;
int tp;

int bytes;
int msgtg;
int tid;

individual *ind_ptr;
tree tmp_tree;

#ifdef PDEBUG
printf ("Receiving\n");
#endif

while ( (bufid = pvm_nrecv(-1, -1)) )
	{
	pvm_bufinfo ( bufid, &bytes, &msgtg, &tid );

	/* Intermediate reset */
	ft = -1;

#ifdef PDEBUG_IMPORT
printf ("GOT FROM tid[%x]\n", tid);
#endif

	switch (msgtg)
		{
		case TERM_REQUEST_TAG:
				{
				/* Force the user termination criterium */
				set_term(1);

				return;

				break;  /* Useless, yes I know. */
				}

		case IND_TAG:
				{
				/* Extract exchange_number and all the trees */
				receive_individual ( &exch, &ind_ptr );

				break;
				}

		case TREE_TAG:
				{
				/* Extract exchange_number, destination pop. the tree and the tree number */
				receive_tree ( &exch, &ft, &tmp_tree );

				break;
				}

		default:
				{
				error (E_FATAL_ERROR, "Unkown tag in exchange_import() value = %d", msgtg);
				}
		}


	/* Reuse the variable "i" from the original code :-) */
	i = exch;

#ifdef PDEBUG_IMPORT
printf ("Exchange nr. %d dest. pop %d from tree %d \n", i, mpop->exch[i].to, ft);
#endif


     {
	  /* where individuals are going. */
          tp = mpop->exch[i].to;

	  /* Check if the population to node mapping was correct */
	  tid = population_mapping (tp);
	  if (tid != pvm_mytid())
		error (E_FATAL_ERROR, "Incorrect mapping! Received msg meant for population number %d\n", tp);


	  /* set up selection method to pick individuals to be replaced. */
          select_con = get_select_context ( mpop->exch[i].tosc );
          tocon = select_con ( SELECT_INIT, NULL, mpop->pop[tp],
                              mpop->exch[i].tosc );

	  /* are we copying whole individuals or creating composites? */
          if ( mpop->exch[i].copywhole > -1 )
          {
	       /*** copying whole individuals. ***/

		    /** pick an individual to be replaced that has not already
		      been replaced during this exchange cycle. **/

		    count = 0;
		    tmp = mpop->pop[tp]->size;

                    do
                    {
                         ti = tocon->select_method ( tocon );
			 count++;

			 if (count > tmp)
				{
				/* No individual left to be selected! */
				printf("exchange_import: no ind. left for replacement 1\n");

				return;
				}
#ifdef PDEBUG_IMPORT_XXXX
printf ("exch: search ind ti %d \n", ti);
printf ("pop size is %d\n", mpop->pop[tp]->size);
#endif
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );

                    /** remove the old iondividual from the population. **/
                    for ( j = 0; j < tree_count; ++j )
                    {
			 /* always dereference ERCs when removing trees
			    from the population. */
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, -1 );
                         free_tree ( mpop->pop[tp]->ind[ti].tr+j );
                    }

		    /* copy the individual. */
#ifdef PDEBUG
printf("dup ind ...");
fflush(stdout);
#endif
                    duplicate_individual ( mpop->pop[tp]->ind+ti, ind_ptr );
#ifdef PDEBUG
printf("dup ind ended\n");
#endif
		    /* Freeing the individual */
		    /* Starting with all the trees themselves */
		    for (k=0; k<tree_count; k++)
			free_tree(ind_ptr->tr+k);

		    /* Free the array */
		    FREE(ind_ptr->tr);

		    /* Free the individual structure */
		    FREE(ind_ptr);

		    /* reference the ERCs in the new individual. */
                    for ( j = 0; j < tree_count; ++j )
                         reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[j].data, 1 );

		    /* mark the individual as just coming from an exchange. */
                    mpop->pop[tp]->ind[ti].flags = FLAG_NEWEXCH;
          }
          else
          {
               /*** creating composite individuals. ***/
               {
		    /** select an individual to be replaced that hasn't already
		      been during this exchange cycle. **/

		    count = 0;
		    tmp = mpop->pop[tp]->size;

                    do
                    {
                         ti = tocon->select_method ( tocon );

			 if (count > tmp)
				{
				/* No individual left to be selected! */
				printf("exchange_import: no ind. left for replacement 2\n");

				return;
				}
                    }
                    while ( mpop->pop[tp]->ind[ti].flags & FLAG_NEWEXCH );
                    

		    /* dereference ERCs in old tree. */
                    reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[ft].data, -1 );
		    /* delete old tree. */
                    free_tree ( mpop->pop[tp]->ind[ti].tr+ft );

		    /* copy new tree. */
                    copy_tree ( mpop->pop[tp]->ind[ti].tr+ft, &tmp_tree);

		    /* Free the tree */
                    free_tree (&tmp_tree);

		    /* reference ERCs in new tree. */
                    reference_ephem_constants ( mpop->pop[tp]->ind[ti].tr[ft].data, 1 );
      		}

          }

	  /* destroy destination selection context. */
          tocon->context_method ( SELECT_CLEAN, tocon, NULL, NULL );
	}

	}

return;
}


/* signal_master()
 *
 * send termination signal to the master
 */

void signal_master(int signal)
{
int ptid;

ptid = pvm_parent();

pvm_initsend(ENCODING);
pvm_send (ptid, signal);

}


/* pack_individual()
 *
 * pack an individual in order to send it
 */

void pack_individual( individual *ind_ptr)
{
int i;

/* First pack the individual's information */
if (pvm_pkdouble(&(ind_ptr->r_fitness), 1, 1))
   pvm_perror ("send_individual() error packing \"r_fitness\"");

if (pvm_pkdouble(&(ind_ptr->s_fitness), 1, 1))
   pvm_perror ("send_individual() error packing \"s_fitness\"");

if (pvm_pkdouble(&(ind_ptr->a_fitness), 1, 1))
   pvm_perror ("send_individual() error packing \"a_fitness\"");

if (pvm_pkint(&(ind_ptr->hits), 1, 1))
   pvm_perror ("send_individual() error packing \"hits\"");

if (pvm_pkint(&(ind_ptr->evald), 1, 1))
   pvm_perror ("send_individual() error packing \"evald\"");

if (pvm_pkint(&(ind_ptr->flags), 1, 1))
   pvm_perror ("send_individual() error packing \"flags\"");



/* 2nd pack its trees */
for (i=0; i<tree_count; i++)
	pack_tree(&(ind_ptr->tr[i]));

return;
}


/* unpack_individual()
 *
 * unpack an individual and allocates the memory for it ...
 */

void unpack_individual ( individual **ind_ptr )
{
int i;

individual *tmp_ind;

tmp_ind = (individual *) MALLOC (sizeof(individual));
tmp_ind->tr = (tree *) MALLOC (sizeof(tree)*tree_count);

/* First unpack the individual's information */
if (pvm_upkdouble(&(tmp_ind->r_fitness), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"r_fitness\"")
;
if (pvm_upkdouble(&(tmp_ind->s_fitness), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"s_fitness\"");

if (pvm_upkdouble(&(tmp_ind->a_fitness), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"a_fitness\"");

if (pvm_upkint(&(tmp_ind->hits), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"hits\"");

if (pvm_upkint(&(tmp_ind->evald), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"evald\"");

if (pvm_upkint(&(tmp_ind->flags), 1, 1))
   pvm_perror ("receive_individual() error unpacking \"flags\"");


/* 2nd unpack it trees */
for (i=0; i<tree_count; i++)
	unpack_tree(&tmp_ind->tr[i]);


/* 3d put into the pointer */
*ind_ptr = tmp_ind;

return;
}


/* send_individual()
 *
 * send an entire individual
 */

void send_individual ( int destination_node, int exch, individual *ind_ptr )
{
#ifdef PDEBUG
printf("send_individiual() to %x\n", destination_node);
#endif

pvm_initsend(ENCODING);

/* Zero pack the exchange number */
if (pvm_pkint(&exch, 1, 1))
   pvm_perror ("send_individual() error packing \"exch\"");


pack_individual(ind_ptr);

/* Send */
if (pvm_send (destination_node, IND_TAG))
   pvm_perror ("send_individual() error sending");


#ifdef PDEBUG
printf("finished send_individual()\n");
#endif
}


/* receive_individual()
 *
 * receives an individual.
 */

void receive_individual ( int *exch, individual **ind_ptr )
{
#ifdef PDEBUG
printf("receive_individiual()\n");
#endif

/* Zero pack the exchange number */
if (pvm_upkint(exch, 1, 1))
   pvm_perror ("send_individual() error unpacking \"exch\"");

/* First unpack the individual's information */
unpack_individual(ind_ptr);

#ifdef PDEBUG
printf("finished receive_individual()\n");
#endif
}

#endif