/*
 * Copyright (c) 2004, University of Massachusetts
 * All Rights Reserved.
 * 
 * Name:       mc.c
 * Date:       23 Aug 2004
 * Revised:    23 Aug 2004
 * Author:     Stephen Siegel <siegel@cs.umass.edu>
 * Maintainer: Stephen Siegel
 * Reader:
 *
 * Compile:    mpicc -o mc mc.c
 * Run:        mpirun -np N mc
 *
 *   N : the number of workers + 3
 *
 * This is an abstraction of the program "A Monte Carlo computation of
 * pi" listed in Figures 3.15, 3.16, 3.17, and 3.18 of "Using MPI".
 * The workers are the first n processes and the random number server
 * is process n+1.  Processes n+2 and n+3 are coordinators used to
 * represent the two call to the collective function MPI_Allreduce.
 */

#include<stdio.h>
#include<stdlib.h>
#include "mpi.h"
#include "../../Profiler.h"

#define REQUEST 1
#define REPLY 2
#define ENTER 1
#define EXIT 1

int main(int argc,char *argv[]) {
  int np, rank, server, coord1, coord2, num_workers;
  MPI_Status status;

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &np);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  num_workers = np - 3; /* procs 0..np-4 */
  server = np - 3;
  coord1 = np - 2;
  coord2 = np - 1;

  if (rank < server) {
    int done = 0;
    int request = 1;
    int scratch;

    while (!done) {
      MPI_Send(&request, 1, MPI_INT, server, REQUEST, MPI_COMM_WORLD);
      request = 1;
      MPI_Recv(&scratch, 1, MPI_INT, server, REPLY, MPI_COMM_WORLD, &status);
      /* The first MPI_Allreduce ... */
      MPI_Send(&scratch, 1, MPI_INT, coord1, ENTER, MPI_COMM_WORLD);
      MPI_Recv(&scratch, 1, MPI_INT, coord1, EXIT, MPI_COMM_WORLD, &status);
      /* The second MPI_Allreduce ... */
      MPI_Send(&scratch, 1, MPI_INT, coord2, ENTER, MPI_COMM_WORLD);
      MPI_Recv(&done, 1, MPI_INT, coord2, EXIT, MPI_COMM_WORLD, &status);
      //printf("Done = %d\n", done);
      request = (done) ? 0 : 1;
      if (rank == 0 ) {
	//printf("Came here request = %d\n", request);
	MPI_Send(&request, 1, MPI_INT, server, REQUEST, MPI_COMM_WORLD);
      }
    }
    fprintf(stdout, "   Worker %d terminating\n", rank);
    fflush(stdout);
  }
  else if (rank == server) {
    int request, rands;

    do {
      MPI_Recv(&request, 1, MPI_INT, MPI_ANY_SOURCE, REQUEST,
	       MPI_COMM_WORLD, &status);
      fprintf(stdout, "Server has received request %d from worker %d\n",
	      request, status.MPI_SOURCE);
      fflush(stdout);
      if (request) {
	MPI_Send(&rands, 1, MPI_INT, status.MPI_SOURCE, REPLY,
		 MPI_COMM_WORLD);
	fprintf(stdout, "Server sent reply to worker %d\n", status.MPI_SOURCE);
      }
    }
    while (request > 0);
    fprintf(stdout, "Server terminating\n");
    fflush(stdout);
  }
  else if (rank == coord1) {
    int i, scratch;

    while (1) {
      for (i = 0; i < num_workers; i++) {
	MPI_Recv(&scratch, 1, MPI_INT, i, ENTER, MPI_COMM_WORLD, &status);
      }
      for (i = 0; i < num_workers; i++) {
	MPI_Send(&scratch, 1, MPI_INT, i, EXIT, MPI_COMM_WORLD);
      }
    }
  }
  else if (rank == coord2) {
    int i, scratch, done = 1;

    while (1) {
      for (i = 0; i < num_workers; i++) {
	MPI_Recv(&scratch, 1, MPI_INT, i, ENTER, MPI_COMM_WORLD, &status);
      }
      done =  (rand()/(RAND_MAX + 1.0) < .9) ? 0 : 1;
      for (i = 0; i < num_workers; i++) {
	MPI_Send(&done, 1, MPI_INT, i, EXIT, MPI_COMM_WORLD);
      }
    }
  }
  temp_Finalize();
}

