//Approximation of the value of pi using the Monte Carlo method
//This method makes a series of random points between (0,0) and (2,2)
//It then checks to see if they are within a circle-radius 1, with center (1,1)
//Finally, it calculates the ratio based on area(circle)/area(square) or
//pi * r * r / 4 = pi / 4 = amount in circle / amount total
//pi = 4 * amount in circle / amount total

//Random numbers are calculated by finding a number between 1 and m and finding
//that number times range / m

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>

int circle_checker(double x, double y)
{
  //checks to see if the point (x, y) is within the bounds of the circle
  //(x - 1)^2 + (y - 1)^2 = 1
  //return value, 0 = not in circle, 1 = in circle
  double upper_y, lower_y; //bounds that y could be in to be in the circle

  //y bounds are calculated using y = 1 +/- SQRT(1 - (x - 1)^2)
  lower_y = 1.0 - sqrt(1 - pow(x - 1, 2));
  upper_y = 1.0 + sqrt(1 - pow(x - 1, 2));

  return((y >= lower_y && y <= upper_y) ? 1 : 0);

}//function circle_checker(double, double)

int main(int argc,char *argv[])
{

  typedef struct sendtype
  {
    int in_circle;
    int procnum;
  } sendtype;
  
  //Variables used for message passing and other MPI stuff
  MPI_Status status;
  int myrank;
  int msgtag;
  int mysize;
  int sent = 0;
  int terminator;
  sendtype Sendtype;

  //Variables used for random number generation
  int seed;       //value sent to processors to begin random number generation
  int a = 200;    //multiplier in random function
  int c = 169;    //added part for random function
  int m = 10000;  //mod part for random function also amount of possible values
  // double rand;       //most recent random number created
  double x;       //random number being calculated based on rand
  double y;       //random number being calculated based on rand

  //Variables used to calculate pi
  int n = 0;    //amount of random points checked by processor
  int n_send = 0; // N which is sent from the workpool
  int count = 0;  //amount of points inside the circle in one processor
  int all_n = 0;  //total amount of points checked at all
  int total = 0;  //total amount of points inside circle for all processors
  double pi;      //final value calculated for pi
 
  //Standard variables for a variety of uses
  int i;

  //Necessary initializing stuff
  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
  MPI_Comm_size(MPI_COMM_WORLD, &mysize);

  if(!myrank)//process 0 - master process
  {
    // Find out what N is
    printf("Please enter the number of points to generate: ");
    scanf("%d", &n);
    terminator = 1;
    //divide up the work and distribute it
    for(i = 0; i < mysize - 1; i++)
    {
      seed = (i + 1) * a + c % m;
      MPI_Send(&seed, 1, MPI_INT, sent + 1, msgtag, MPI_COMM_WORLD);
      MPI_Send(&terminator, 1, MPI_INT, sent + 1, msgtag, MPI_COMM_WORLD);
      sent++;
      n_send++;
    }
  
    do {
      MPI_Recv(&Sendtype, 2, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
      total += Sendtype.in_circle;
  
      sent--;
      if (n_send < n) {
	MPI_Send(&terminator, 1, MPI_INT, Sendtype.procnum, msgtag, MPI_COMM_WORLD);
        sent++;
	n_send++;  
      }
      else { 
	terminator = 0;
	MPI_Send(&terminator, 1, MPI_INT, Sendtype.procnum, msgtag, MPI_COMM_WORLD);
        printf("sent terminator to %d\n",Sendtype.procnum);
      }
    } while (sent > 0);

    //estimate pi based on equation pi = 4 * amount in circle / amount total
    pi = (4.0 * total) / (double)n;
    printf("pi = %f\n", pi);    
  }
  else//all other processes
  {
    MPI_Recv(&seed, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    MPI_Recv(&terminator, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    srand(seed);
    printf("got to here too\n");

    while (terminator)
    {
      x = 2.0 * rand()/(pow(2,15)-1.0);
      y = 2.0 * rand()/(pow(2,15)-1.0);
      //check to see if the point is inside the circle
      Sendtype.in_circle = circle_checker(x, y);
      Sendtype.procnum = myrank;
      //send back amount inside circle (n is constant)
       MPI_Send(&Sendtype, 2, MPI_INT, 0, msgtag, MPI_COMM_WORLD);
       MPI_Recv(&terminator, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
       if(!terminator)
	 {
	   printf("terminating process %d\n",myrank);
         }
    }  
}

  //Necessary finalizing stuff
 
  MPI_Barrier(MPI_COMM_WORLD);

 MPI_Finalize();

}//function main(int, char*)







