/* MPI sample program; NOT INTENDED TO BE EFFICIENT as a prime
   finder, either in algorithm or implementation 

   MPI (Message Passing Interface) is a popular package using
   the "message passing" paradigm for communicating between
   processors in parallel applications; as the name implies,
   processors communicate by passing messages using "send" and 
   "receive" functions

   finds and reports the number of primes less than or equal to N

   uses a pipeline approach:  node 0 looks at all the odd numbers
   (i.e. has already done filtering out of multiples of 2) and
   filters out those that are multiples of 3, passing the rest
   to node 1; node 1 filters out the multiples of 5, passing
   the rest to node 2; in this simple example, we just have node
   2 filter out all the rest and then report the number of primes 

   note that we should NOT have a node run through all numbers
   before passing them on to the next node, since we would then
   have no parallelism at all; on the other hand, passing on just
   one number at a time isn't efficient either, due to the high
   overhead of sending a message if it is a network (tens of
   microseconds until the first bit reaches the wire, due to
   software delay); thus efficiency would be greatly improved if 
   each node saved up a chunk of numbers before passing them to 
   the next node */

// this include file is mandatory 
#include <mpi.h>

#define MAX_N 100000
#define PIPE_MSG 0  // type of message containing a number to 
                       be checked
#define END_MSG 1  // type of message indicating no more data will
                      be coming 

int NNodes,  /* number of nodes in computation*/
    N,  /* find all primes from 2 to N */
    Me,  /* my node number */
    ToCheck;  /* current number to check for passing on to next node;
                 stylistically this might be nicer as a local in
                 Node*(), but I have placed it here to dramatize
                 the fact that the globals are NOT shared among
                 the nodes */

double T1,T2;  /* start and finish times */

Init(Argc,Argv)
   int Argc;  char **Argv;

{  int DebugWait;  

   N = atoi(Argv[1]); 
   DebugWait = atoi(Argv[2]);

   /* this loop is here to synchronize all nodes for debugging;
      if DebugWait is specified as 1 on the command line, all nodes 
      wait here until the debugging programmer starts GDB at all 
      nodes and within GDB sets DebugWait to 0 to then proceed */
   while (DebugWait) ;

   /* mandatory to begin any MPI program */
   MPI_Init(&Argc,&Argv);

   /* puts the number of nodes in NNodes */
   MPI_Comm_size(MPI_COMM_WORLD,&NNodes);
   /* puts the node number of this node in Me */
   MPI_Comm_rank(MPI_COMM_WORLD,&Me); 

   /* OK, get started; first record current time in T1 */
   if (Me == 2) T1 = MPI_Wtime();
}

Node0()

{  int I,Dummy,
       Error;  /* not checked in this example */
   for (I = 1; I <= N/2; I++)  {
      ToCheck = 2 * I + 1;
      if (ToCheck > N) break;
      /* MPI_Send  --  send a message
               parameters:
               pointer to place where message is to be drawn from
               number of items in message
               item type
               destination node
               message type ("tag") programmer-defined
               node group number (in this case all nodes) */
      if (ToCheck % 3 > 0)
         Error = MPI_Send(&ToCheck,1,MPI_INT,1,PIPE_MSG,MPI_COMM_WORLD);
   }
   Error = MPI_Send(&Dummy,1,MPI_INT,1,END_MSG,MPI_COMM_WORLD);
}

Node1()

{  int Error,  /* not checked in this example */
       Dummy;
   MPI_Status Status;  /* see below */ 

   while (1)  {
      /* MPI_Recv  --  receive a message
               parameters:
               pointer to place to store message
               number of items in message (see notes on
                  this at the end of this file)
               item type
               accept message from which node(s) 
               message type ("tag"), programmer-defined (in this
          case any type)
               node group number (in this case all nodes)
               status (see notes on this at the end of this file) */
      Error = MPI_Recv(&ToCheck,1,MPI_INT,0,MPI_ANY_TAG,
                  MPI_COMM_WORLD,&Status);
      if (Status.MPI_TAG == END_MSG) break;
      if (ToCheck % 5 > 0) 
         Error = MPI_Send(&ToCheck,1,MPI_INT,2,PIPE_MSG,MPI_COMM_WORLD);
   }
   /* now send our end-of-data signal, which is conveyed in the
      message type, not the message (we have a dummy message just
      as a placeholder */
   Error = MPI_Send(&Dummy,1,MPI_INT,2,END_MSG,MPI_COMM_WORLD);
}

Node2()

{  int ToCheck,  /* current number to check from Node 0 */
       Error,  /* not checked in this example */
       PrimeCount,I,IsComposite;
   MPI_Status Status;  /* see below */ 

   PrimeCount = 3;  /* must account for the primes 2, 3 and 5, which
                       won't be detected below */
   while (1)  {
      Error = MPI_Recv(&ToCheck,1,MPI_INT,1,MPI_ANY_TAG,
                  MPI_COMM_WORLD,&Status);
      if (Status.MPI_TAG == END_MSG) break;
      IsComposite = 0;
      for (I = 7; I*I <= ToCheck; I += 2)
         if (ToCheck % I == 0)  {
    IsComposite = 1;
    break;
 }
      if (!IsComposite) PrimeCount++;  
   }
   /* check the time again, and subtract to find run time */
   T2 = MPI_Wtime();
   printf("elapsed time = %f\n",(float)(T2-T1));
   /* print results */
   printf("number of primes = %d\n",PrimeCount);
}

main(argc,argv)
   int argc; char **argv;

{  Init(argc,argv);
   /* note:  instead of having a switch statement, we could write
      three different programs, each running on a different node */
   switch (Me)  {
      case 0:  Node0();
               break;
      case 1:  Node1();
               break;
      case 2:  Node2();
   };
   /* mandatory for all MPI programs */
   MPI_Finalize();
}

/* explanation of "number of items" and "status" arguments at the end 
   of MPI_Recv():

   when receiving a message you must anticipate the longest possible
   message, but the actual received message may be much shorter than
   this; you can call the MPI_Get_count() function on the status
   argument to find out how many items were actually received

   the status argument will be a pointer to a struct, containing the
   node number, message type and error status of the received
   message

   say our last parameter is Status; then Status.MPI_SOURCE
   will contain the number of the sending node, and 
   Status.MPI_TAG will contain the message type; these are
   important if used MPI_ANY_SOURCE or MPI_ANY_TAG in our
   node or tag fields but still have to know who sent the
   message or what kind it is */
