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

int main(int argc, char* argv[]) {
		int my_rank,			/* rank of process */
			size,				/* number of processes */
			source,				/* rank of sender */
			dest,				/* rank of receiver */
			tag = 0,			/* tag for messages */
			len, i;
		double inp, start_time, end_time;
		char name[100],			/* Processor name */
			message[100];
		MPI_Status status;		/* return status for receive */

		MPI_Init(&argc, &argv);	/* Start up MPI */
		MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
								/* Find out process rank */
		if (!argv[1]) {
				if (my_rank == 0) 
						printf("Usage: (MPI stuff) %s (1|2|3)\n", argv[0]);
				MPI_Finalize();
				return 1;
		}
		MPI_Comm_size(MPI_COMM_WORLD, &size);
								/* Find out number of processes */
		MPI_Get_processor_name(name, &len);
		if (my_rank == 0) {
				printf("Please enter a value: ");
				scanf("%lf", &inp);
		}
		switch (atoi(argv[1])) {
		case 1:					/* Version 1: Send from 0 to all the others
								 * using MPI_Send() and MPI_Recv. */
				if (my_rank == 0) {
						start_time = MPI_Wtime();
						
						for(i=1; i < size; i++) {
								printf("Process 0 sending %f to process %d\n",
												inp, i);
								MPI_Send(&inp, 1, MPI_DOUBLE, i, tag,
												MPI_COMM_WORLD);
						}
						
						for(i = 1; i < size; i++) {
								MPI_Recv(message, 100, MPI_CHAR, i, tag,
												MPI_COMM_WORLD, &status);
								printf("%s\n", message);
						}
						end_time = MPI_Wtime();
						printf("Total time for sending/receiving data: %f\n",
										end_time - start_time);
						
				} else {		/* Process other than root */
						MPI_Recv(&inp, 1, MPI_DOUBLE, 0, tag, MPI_COMM_WORLD,
										&status);
						sprintf(message, 
								"Process %d: received %f on machine %s",
								my_rank, inp, name);
						MPI_Send(message, strlen(message)+1, MPI_CHAR, 0, tag,
										MPI_COMM_WORLD);
				}
				break;
		case 2:					/* Version 2: Use MPI_Bcast to accomplish same
								 * thing as Version 1 */
				start_time = MPI_Wtime();
				MPI_Bcast(&inp, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
				if (my_rank == 0) {
						printf("Process 0 sending %f to all processes...\n",
										inp);
						for(i = 1; i < size; i++) {
								MPI_Recv(message, 100, MPI_CHAR, i, tag,
												MPI_COMM_WORLD, &status);
								printf("%s\n", message);
						}
						end_time = MPI_Wtime();
						printf("Total time for sending/receiving data: %f\n",
										end_time - start_time);
				} else {
						sprintf(message, 
								"Process %d: received %f on machine %s",
								my_rank, inp, name);
						MPI_Send(message, strlen(message)+1, MPI_CHAR, 0, tag,
										MPI_COMM_WORLD);
				}
				break;
		case 3:
				if (my_rank == 0) {
						start_time = MPI_Wtime();
						printf("Process 0 sending %f to process 1\n", inp);
						MPI_Send(&inp, 1, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
						for(i = 1; i < size; i++) {
								for (dest = 0; dest < 2; dest++) {
										MPI_Recv(message, 100, MPI_CHAR, i, 
														tag, MPI_COMM_WORLD,
														&status);
										printf("%s\n", message);
								}
						}
						end_time = MPI_Wtime();
						printf("Total time for sending/receiving data: %f\n",
										end_time - start_time);
				} else {
						MPI_Recv(&inp, 1, MPI_DOUBLE, my_rank-1, tag, 
										MPI_COMM_WORLD, &status);
						sprintf(message, "Process %d: Received %f on %s",
										my_rank, inp, name);
						MPI_Send(message, strlen(message)+1, MPI_CHAR, 0, tag,
										MPI_COMM_WORLD);
						if (my_rank != size-1) {
								sprintf(message,
										"Process %d sending %f to process %d",
										my_rank, inp, my_rank+1);
								MPI_Send(message, strlen(message)+1, MPI_CHAR,
												0, tag, MPI_COMM_WORLD);
								MPI_Send(&inp, 1, MPI_DOUBLE, my_rank+1, tag,
												MPI_COMM_WORLD);
						} else {
								sprintf(message, 
										"Process %d says we\'re done",
										my_rank);
								MPI_Send(message, strlen(message)+1, MPI_CHAR,
												0, tag, MPI_COMM_WORLD);
						}
				}
				break;
		default:
				if (my_rank == 0) printf("Err, what?\n");
		}

		MPI_Finalize();			/* Shut down MPI */
		return 0;
}

