/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *  (C) 2012 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */
#include "mpi.h" 
#include "stdio.h"
#include "stdlib.h"
#include "mpitest.h"

/* tests passive target RMA on 2 processes. tests the lock-single_op-unlock 
   optimization for less common cases:

   origin datatype derived, target datatype predefined

*/
int main(int argc, char *argv[]) 
{ 
    int          wrank, nprocs, *srcbuf, *rmabuf, i;
    int          memsize;
    MPI_Datatype vectype;
    MPI_Win      win;
    int          errs = 0;

    MTest_Init(&argc,&argv); 
    MPI_Comm_size(MPI_COMM_WORLD,&nprocs); 
    MPI_Comm_rank(MPI_COMM_WORLD,&wrank); 

    if (nprocs < 2) {
        printf("Run this program with 2 or more processes\n");
        MPI_Abort(MPI_COMM_WORLD, 1);
    }

    memsize = 10 * 4 * nprocs;
    /* Create and initialize data areas */
    srcbuf = (int *)malloc( sizeof(int) * memsize );
    MPI_Alloc_mem( sizeof(int) * memsize, MPI_INFO_NULL, &rmabuf );
    if (!srcbuf || !rmabuf) {
	printf( "Unable to allocate srcbuf and rmabuf of size %d\n", memsize );
	MPI_Abort( MPI_COMM_WORLD, 1 );
    }
    for (i=0; i<memsize; i++) {
      rmabuf[i] = -i;
      srcbuf[i] = i;
    }

    MPI_Win_create( rmabuf, memsize*sizeof(int), sizeof(int), MPI_INFO_NULL, 
		    MPI_COMM_WORLD, &win );

    /* Vector of 10 elements, separated by 4 */
    MPI_Type_vector( 10, 1, 4, MPI_INT, &vectype );
    MPI_Type_commit( &vectype );

    /* Accumulate with a derived origin type and target predefined type*/
    if (wrank == 0) {
	MPI_Barrier( MPI_COMM_WORLD );
	MPI_Win_lock( MPI_LOCK_EXCLUSIVE, 0, 0, win );
	for (i=0; i<10; i++) {
	    if (rmabuf[i] != -i + 4*i) {
		errs++;
		printf( "Acc: expected rmabuf[%d] = %d but saw %d\n", 
			i, -i + 4*i, rmabuf[i] );
	    }
	    rmabuf[i] = -i;
	}
	for (i=10; i<memsize; i++) {
	    if (rmabuf[i] != -i) {
		errs++;
		printf( "Acc: expected rmabuf[%d] = %d but saw %d\n", 
			i, -i, rmabuf[i] );
		rmabuf[i] = -i;
	    }
	}
	MPI_Win_unlock( 0, win );
    }
    else if (wrank == 1) {
	MPI_Win_lock( MPI_LOCK_SHARED, 0, 0, win );
	MPI_Accumulate( srcbuf, 1, vectype, 0, 0, 10, MPI_INT, MPI_SUM, win );
	MPI_Win_unlock( 0, win );
	MPI_Barrier( MPI_COMM_WORLD );
    }
    else {
	MPI_Barrier( MPI_COMM_WORLD );
    }

    MPI_Barrier(MPI_COMM_WORLD);

    /* Put with a derived origin type and target predefined type*/
    if (wrank == 0) {
	MPI_Barrier( MPI_COMM_WORLD );
	MPI_Win_lock( MPI_LOCK_EXCLUSIVE, 0, 0, win );
	for (i=0; i<10; i++) {
	    if (rmabuf[i] != 4*i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, 4*i, rmabuf[i] );
	    }
	    rmabuf[i] = -i;
	}
	for (i=10; i<memsize; i++) {
	    if (rmabuf[i] != -i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, -i, rmabuf[i] );
		rmabuf[i] = -i;
	    }
	}
	MPI_Win_unlock( 0, win );
    }
    else if (wrank == 1) {
	MPI_Win_lock( MPI_LOCK_SHARED, 0, 0, win );
	MPI_Put( srcbuf, 1, vectype, 0, 0, 10, MPI_INT, win );
	MPI_Win_unlock( 0, win );
	MPI_Barrier( MPI_COMM_WORLD );
    }
    else {
	MPI_Barrier( MPI_COMM_WORLD );
    }

    MPI_Barrier(MPI_COMM_WORLD);

    /* Put with a derived origin type and target predefined type, with 
       a get (see the move-to-end optimization) */
    if (wrank == 0) {
	MPI_Barrier( MPI_COMM_WORLD );
	MPI_Win_lock( MPI_LOCK_EXCLUSIVE, 0, 0, win );
	for (i=0; i<10; i++) {
	    if (rmabuf[i] != 4*i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, 4*i, rmabuf[i] );
	    }
	    rmabuf[i] = -i;
	}
	for (i=10; i<memsize; i++) {
	    if (rmabuf[i] != -i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, -i, rmabuf[i] );
		rmabuf[i] = -i;
	    }
	}
	MPI_Win_unlock( 0, win );
    }
    else if (wrank == 1) {
	int val;
	MPI_Win_lock( MPI_LOCK_SHARED, 0, 0, win );
	MPI_Get( &val, 1, MPI_INT, 0, 10, 1, MPI_INT, win );
	MPI_Put( srcbuf, 1, vectype, 0, 0, 10, MPI_INT, win );
	MPI_Win_unlock( 0, win );
	MPI_Barrier( MPI_COMM_WORLD );
	if (val != -10) {
	    errs++;
	    printf( "Get: Expected -10, got %d\n", val );
	}
    }
    else {
	MPI_Barrier( MPI_COMM_WORLD );
    }

    MPI_Barrier(MPI_COMM_WORLD);

    /* Put with a derived origin type and target predefined type, with 
       a get already at the end (see the move-to-end optimization) */
    if (wrank == 0) {
	MPI_Barrier( MPI_COMM_WORLD );
	MPI_Win_lock( MPI_LOCK_EXCLUSIVE, 0, 0, win );
	for (i=0; i<10; i++) {
	    if (rmabuf[i] != 4*i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, 4*i, rmabuf[i] );
	    }
	    rmabuf[i] = -i;
	}
	for (i=10; i<memsize; i++) {
	    if (rmabuf[i] != -i) {
		errs++;
		printf( "Put: expected rmabuf[%d] = %d but saw %d\n", 
			i, -i, rmabuf[i] );
		rmabuf[i] = -i;
	    }
	}
	MPI_Win_unlock( 0, win );
    }
    else if (wrank == 1) {
	int val;
	MPI_Win_lock( MPI_LOCK_SHARED, 0, 0, win );
	MPI_Put( srcbuf, 1, vectype, 0, 0, 10, MPI_INT, win );
	MPI_Get( &val, 1, MPI_INT, 0, 10, 1, MPI_INT, win );
	MPI_Win_unlock( 0, win );
	MPI_Barrier( MPI_COMM_WORLD );
	if (val != -10) {
	    errs++;
	    printf( "Get: Expected -10, got %d\n", val );
	}
    }
    else {
	MPI_Barrier( MPI_COMM_WORLD );
    }

    MPI_Win_free( &win );
    MPI_Free_mem( rmabuf );
    free( srcbuf );
    MPI_Type_free( &vectype );

    MTest_Finalize(errs);
    MPI_Finalize(); 
    return 0; 
} 

