/* Sequential execution code */
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>

static int verbose = 0;

typedef struct {
  MPI_Comm lcomm;
  int      prevRank, nextRank;
} seqInfo;

static int seqKeyval = MPI_KEYVAL_INVALID;

static int seqDelFn( MPI_Comm comm, int keyval, void *attr, void *estate )
{
  seqInfo *myinfo = (seqInfo *)attr;
  if (verbose) printf( "About to free communicator %d\n", myinfo->lcomm );

  MPI_Comm_free( &myinfo->lcomm );
  free( myinfo );
  return 0;
}

#if 0
/* This is an option to explain the copy function */
static int seqCopyFn( MPI_Comm comm, int keyval, void *attr, void *estate )
{
  seqInfo *myinfo = (seqInfo *)attr;
  /* preload the duplicated communicator */

  return 0;
}
#endif

void seqBegin( MPI_Comm comm )
{
  MPI_Comm lcomm;
  int      flag, mysize, myrank;
  seqInfo  *info;
  if (seqKeyval == MPI_KEYVAL_INVALID) {
    MPI_Comm_create_keyval( MPI_NULL_COPY_FN, seqDelFn, &seqKeyval, NULL );
  }
  MPI_Comm_get_attr( comm, seqKeyval, &info, &flag );
  if (!flag) {
    info = (seqInfo *)malloc( sizeof(seqInfo) );
    MPI_Comm_dup( comm, &info->lcomm );
    MPI_Comm_rank( info->lcomm, &myrank );
    MPI_Comm_size( info->lcomm, &mysize );
    info->prevRank = myrank - 1;
    if (info->prevRank < 0)   info->prevRank = MPI_PROC_NULL;
    info->nextRank = myrank + 1;
    if (info->nextRank >= mysize) info->nextRank = MPI_PROC_NULL;
    if (verbose) {
      printf( "seqbegin: prev = %d, next = %d\n",
	      info->prevRank, info->nextRank );
    }
    MPI_Comm_set_attr( comm, seqKeyval, info );
  }
  MPI_Recv( NULL, 0, MPI_INT, info->prevRank, 0, info->lcomm,
	    MPI_STATUS_IGNORE );
}

void seqEnd( MPI_Comm comm )
{
  seqInfo *info;
  int     flag;

  /* Sanity check */
  if (seqKeyval == MPI_KEYVAL_INVALID)
    MPI_Abort( MPI_COMM_WORLD, 1 );
  MPI_Comm_get_attr( comm, seqKeyval, &info, &flag );
  if (!info || !flag)
    MPI_Abort( MPI_COMM_WORLD, 1 );
  if (verbose) {
    printf( "seqend: prev = %d, next = %d\n",
	    info->prevRank, info->nextRank );
  }
  MPI_Send( NULL, 0, MPI_INT, info->nextRank, 0, info->lcomm );

  /* Make everyone wait until all have completed their send */
  MPI_Barrier( info->lcomm );
}

/* The process with prev == MPI_PROC_NULL goes first */
void seqChangeOrder( MPI_Comm comm, int prev, int next )
{
  seqInfo *info;
  int flag;
  /* Sanity check */
  if (seqKeyval == MPI_KEYVAL_INVALID)
    MPI_Abort( MPI_COMM_WORLD, 1 );
  MPI_Comm_get_attr( comm, seqKeyval, &info, &flag );
  if (!info || !flag)
    MPI_Abort( MPI_COMM_WORLD, 1 );
  /* Update the order */
  info->prevRank = prev;
  info->nextRank = next;
}