
/* file containing function to set up master and also function to liase with
slave processes on other machines via sockets (i.e. to farm out the
individual evaluations in the SE to them).  At the beginning of this
function call all genetic_fitness values are undetermined, at the end of it
they have all been calculated */  


#include <math.h>
#include <stdio.h>

#include "master.h"

main()
{

  /* set everything up */
  set_up_all();

  /* leave the master function to farm out all the evaluations to slave
  processes */
  master();

  /* print out the final results */
  print_results();
}


/* function to set everything up */
void set_up_all()
{

  /* get a random start for the random number sequence */
  set_rand_seed();

  /* set up the communications to slaves */
  init_master();
}


/* function to pass out all individuals to slaves, in order for their
genetic fitnesses to be calculated more quickly there */
void master()
{

  curr_indvdl = 0;
  num_indvdls_calculated = 0;

  /* this loop continues until all individuals have been calculated */
  while (num_indvdls_calculated < TOTAL_NUM_TESTS) {

	/* get the number of another slave that is waiting to receive work
	(function keeps on trying until there is one) */
	slave_socket_id = activate_master_socket(master_socket_id);

	/* read the result packet from the slave and use it appropriately */
	get_and_process_slave_result(slave_socket_id);

	/* find next individual needing calculating */
	if (num_indvdls_left_to_farm_out() > 0) {
	  /* find the next individual needing to be farmed out */
	  curr_indvdl = (curr_indvdl+1) % TOTAL_NUM_TESTS;
	  while ((done_indvdls[curr_indvdl] == TRUE) ||
			 (started_indvdls[curr_indvdl] > 2))
		curr_indvdl = (curr_indvdl+1) % TOTAL_NUM_TESTS;
	}
	else {
	  /* send a dummy individual to signal that the slave should wait a
	  while and then try again to see if there is work for it */
	  curr_indvdl = -1;
	}

	/* farm some more work out to the slave (give it another individual) */
	give_slave_more_work(slave_socket_id, curr_indvdl);

	fflush(stdout);
  }

  /* all individuals now calculated so finish */
}


void init_master()
{

  int i;

  /* close this portnum in case it has been left open by a previous process */
  close(MASTER_PORTNUM);

  /* create the master socket */
  master_socket_id = make_master_socket(MASTER_PORTNUM);

  printf("master socket created\n");

  /* initialise arrays */
  for (i = 0; i < TOTAL_NUM_TESTS; i++) {
	done_indvdls[i] = FALSE;
	started_indvdls[i] = 0;
    types[i] = 0;
	g[i] = 0.0;
	l[i] = 0.0;
  }
}


/* function to get a results packet from a slave and process it correctly */
void get_and_process_slave_result(slave_id)
int slave_id;			/* id of socket on which slave is connected */
{

  int b_c;

  b_c = get_message(slave_id, &reply_packet, sizeof(struct gf_struct));

  /* three possible messages from slave: (i) initial registration message
  (g_f = -1.0), (ii) usable result, or (iii) a result already calculated by
  another slave */ 
  if (reply_packet.gf < 0.0) {

	/* do nothing */
  }

  else {		/* g_f >= 0.0 */

	/* if useful result */
	if (done_indvdls[reply_packet.indvdl_number] == FALSE) {

	  g[reply_packet.indvdl_number] = reply_packet.gf;
	  l[reply_packet.indvdl_number] = reply_packet.lifespan;
	  types[reply_packet.indvdl_number] = reply_packet.slave_type;

	  printf("g_f[%d] = %g, lifespan = %g (type = %d)\n",
		reply_packet.indvdl_number, 
		g[reply_packet.indvdl_number], l[reply_packet.indvdl_number],
		types[reply_packet.indvdl_number]);

	  done_indvdls[reply_packet.indvdl_number] = TRUE;

	  num_indvdls_calculated++;
	}
	else {	/* result already calculated */

	  /* do nothing */
	}
  }
}


/* function to farm some more work out to the slave (give it another
individual) */
void give_slave_more_work(slave_id, curr_indvdl)
int slave_id;					/* id of socket on which to write to slave */
int curr_indvdl;				/* number of individual for slave to work on */
{

  int b_c;
  double rnd_number;

  if (curr_indvdl == -1) {

	work_packet.indvdl_number = -1;
  }
  else {

	work_packet.indvdl_number = curr_indvdl;
#ifdef DOSTATS
	rnd_number = rnd(1.0);
	if ((rnd_number > 0.00) && (rnd_number < 0.25))
	  work_packet.slave_type = STD;
	else if ((rnd_number > 0.25) && (rnd_number < 0.50))
	  work_packet.slave_type = V1;
	else if ((rnd_number > 0.50) && (rnd_number < 0.75))
	  work_packet.slave_type = V2;
	else if ((rnd_number > 0.75) && (rnd_number < 1.00))
	  work_packet.slave_type = V3;
	else
	  printf("bad random number\n");
#else
	work_packet.slave_type = STD;
#endif  //DOSTATS
  }

  b_c = send_message(slave_id, &work_packet, sizeof(struct work_struct));

  /* break connection to slave */
  close(slave_socket_id);

  started_indvdls[curr_indvdl]++;
}


/* function to calculate and return the number of individuals in the
population which still need to be sent to a slave (they haven't been
calculated yet and haven't already been sent to three slaves) */
int num_indvdls_left_to_farm_out()
{

  int i;
  int counter = 0;

  /* calculate how many individuals are left to be farmed out */
  for (i = 0; i < TOTAL_NUM_TESTS; i ++)
	if ((done_indvdls[i] == FALSE) && (started_indvdls[i] < 3))
	  counter++;;

  return(counter);
}  


/* print out the final results */
void print_results()
{

  FILE *f;
  int i;
  double sum_perf, av_perf;

  printf("\n# testing of ASM finished:\n\n");

  /* calculate average performance */
  sum_perf = 0.0;
  for (i = 0; i < TOTAL_NUM_TESTS; i++)
	sum_perf += g[i];
  av_perf = sum_perf / ((double) TOTAL_NUM_TESTS);

  /* print results out to file */
  f = fopenwithcheck("test_results", "w");
  fprintf(f, "%d tests with an average fitness of %.4f\n", TOTAL_NUM_TESTS,
	av_perf);
  for (i = 0; i < TOTAL_NUM_TESTS; i++) {
	fprintf(f, "lspan %.6f, genetic fitness %.6f, type %d\n", l[i], g[i],
	  types[i]);
  }
  fclose(f);
}
