/*
** User-defined reduce function to check a sorted list
** This one uses a derived data type
** Copyright Rolf Riesen 2008
**
*/
#include <stdio.h>
#include <mpi.h>

#define TRUE	(1)
#define FALSE	(0)
#define ELTS	(3) /* # elemtns in data structure */


/*
** Local functions
*/
void my_func(void *invec, void *inoutvec, int *len, MPI_Datatype *dt);


/*
** Globals
*/
int my_rank, nproc;

typedef struct sorted_t   {
    int unsorted;
    int max;
    int min;
} sorted_t;


int
main(int argc, char *argv[])
{

sorted_t my_value;
sorted_t result;
int root;
int count;
MPI_Op my_op;

int blocklengths[ELTS];
MPI_Datatype types[ELTS];
MPI_Aint displacements[ELTS];

MPI_Datatype my_value_t;

MPI_Aint mpi_addr_struct;
MPI_Aint mpi_addr_struct_unsorted;
MPI_Aint mpi_addr_struct_max;
MPI_Aint mpi_addr_struct_min;



    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    /* Each node contributes its own node number */
    my_value.unsorted= FALSE;
    my_value.min= my_rank;
    my_value.max= my_rank;

    #ifdef DEBUG
    if (my_rank == (nproc - 1))   {
	my_value.min= 0;
	my_value.max= 0;
    }
    #endif /* DEBUG */

    root= 0;
    count= 1;

    MPI_Op_create(my_func, FALSE, &my_op);

    /* Create a new MPI data type */
    MPI_Get_address(&my_value, &mpi_addr_struct);
    MPI_Get_address(&my_value.unsorted, &mpi_addr_struct_unsorted);
    MPI_Get_address(&my_value.max, &mpi_addr_struct_max);
    MPI_Get_address(&my_value.min, &mpi_addr_struct_min);

    types[0]= MPI_INT;
    types[1]= MPI_INT;
    types[2]= MPI_INT;

    blocklengths[0]= 1;         /* 1 int */
    blocklengths[1]= 1;         /* 1 int */
    blocklengths[2]= 1;         /* 1 int */

    displacements[0]= 0;
    displacements[1]= mpi_addr_struct_max - mpi_addr_struct;
    displacements[2]= mpi_addr_struct_min - mpi_addr_struct;

    MPI_Type_create_struct(ELTS, blocklengths, displacements,
            types, &my_value_t);

    MPI_Type_commit(&my_value_t);


    /* Do an integer reduction */
    MPI_Reduce(&my_value, &result, count, my_value_t, my_op, root,
            MPI_COMM_WORLD);

    if (my_rank == root)   {
	printf("[%3d] root received min %d, max %d, unsorted %d\n",
	    my_rank, result.min, result.max, result.unsorted);
    }

    MPI_Op_free(&my_op);
    MPI_Finalize();
    return 0;

}  /* end of main() */



void
my_func(void *invec, void *inoutvec, int *len, MPI_Datatype *dt)
{

sorted_t *left, *right;
sorted_t *out;


    left= (sorted_t *)invec;
    right= (sorted_t *)inoutvec;
    out= (sorted_t *)inoutvec;

    if (left->unsorted || right->unsorted)   {
	/* We're not sorted */
	out->unsorted= TRUE;
	printf("[%3d] Not sorted (from flag)\n", my_rank);
	return;
    }

    if (left->max <= right->min)   {
	out->min= left->min;
	out->max= right->max;
    }   else   {
	/* We're not sorted */
	out->unsorted= TRUE;
	printf("[%3d] Not sorted (from values)\n", my_rank);
    }

    printf("my_func() rank %d, min %d, max %d\n", my_rank, out->min, out->max);

}  /* end of my_func() */
