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

int main( argc, argv )
int argc;
char **argv;
{
    int rank, size;
    MPI_Comm new_comm;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_split( MPI_COMM_WORLD, rank == 0, 0, &new_comm );
    if (rank == 0) 
	master_io( MPI_COMM_WORLD, new_comm );
    else
	slave_io( MPI_COMM_WORLD, new_comm );

    MPI_Finalize( );
    return 0;
}

#define MSG_EXIT 1
#define MSG_PRINT_ORDERED 2
#define MSG_PRINT_UNORDERED 3

/* This is the master */
int master_io( master_comm, comm )
MPI_Comm comm;
{
    int        i,j, size, nslave, firstmsg;
    char       buf[256], buf2[256];
    MPI_Status status;

    MPI_Comm_size( master_comm, &size );
    nslave = size - 1;
    while (nslave > 0) {
	MPI_Recv( buf, 256, MPI_CHAR, MPI_ANY_SOURCE, MPI_ANY_TAG, 
		  master_comm, &status );
	switch (status.MPI_TAG) {
	case MSG_EXIT: nslave--; break;
	case MSG_PRINT_UNORDERED:
	    fputs( buf, stdout );
	    break;
	case MSG_PRINT_ORDERED:
	    /* This lets us get any of the ordered messages first, and then
	       start printing them.  There are other ways to do this
	       (see related exercises) */
	    firstmsg = status.MPI_SOURCE;
	    for (i=1; i<size; i++) {
		if (i == firstmsg) 
		    fputs( buf, stdout );
		else {
		    MPI_Recv( buf2, 256, MPI_CHAR, i, MSG_PRINT_ORDERED, 
			  master_comm, &status );
		    fputs( buf2, stdout );
		}
	    }
	    break;
	}
    }
    return 0;
}

/* This is the slave */
int slave_io( master_comm, comm )
MPI_Comm comm;
{
    char buf[256];
    int  rank;
    
    MPI_Comm_rank( comm, &rank );
    sprintf( buf, "Hello from slave %d\n", rank );
    MPI_Send( buf, strlen(buf) + 1, MPI_CHAR, 0, MSG_PRINT_ORDERED, 
	      master_comm );
    
    sprintf( buf, "Goodbye from slave %d\n", rank );
    MPI_Send( buf, strlen(buf) + 1, MPI_CHAR, 0, MSG_PRINT_ORDERED, 
	      master_comm );

    sprintf( buf, "I'm exiting (%d)\n", rank );
    MPI_Send( buf, strlen(buf) + 1, MPI_CHAR, 0, MSG_PRINT_UNORDERED, 
	      master_comm );

    MPI_Send( buf, 0, MPI_CHAR, 0, MSG_EXIT, master_comm );
    return 0;
}