/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Universite Bordeaux 1, Telecom SudParis
 * See COPYING in top-level directory.
 */

#ifndef MPI_EZTRACE_H
#define MPI_EZTRACE_H

#include <mpi.h>
#include <stdint.h>
#include <stdlib.h>
#include <eztrace-lib/eztrace.h>

#if MPI_VERSION >= 3
/* Use MPI 3 */

/* In MPI3, the prototype of some MPI functions have change.
 * For instance, in MPI2.X, the prototype of MPI_Send was:
 * int MPI_Send(void* buffer, [...]);
 * In MPI3, MPI_Send is defined as:
 * int MPI_Send(const void* buffer, [...]);
 * In order to fix this prototype issue, let's define a CONST macro
 */
#define CONST const

#ifndef USE_MPI3
#define USE_MPI3
#endif

#else
/* This is MPI 2.X */

#define CONST

#ifdef USE_MPI3
#undef USE_MPI3
#endif

#endif


#ifndef MPI_MODULE_NAME
#define MPI_MODULE_NAME mpi
#endif

#define CURRENT_MODULE MPI_MODULE_NAME
DECLARE_CURRENT_MODULE;

/* maximum number of items to be allocated statically
 * if the application need more than this, a dynamic array
 * is allocated using malloc()
 */
#define MAX_REQS 128

/* allocate a number of elements using a static array if possible
 * if not possible (ie. count>MAX_REQS) use a dynamic array (ie. malloc)
 */
#define ALLOCATE_ITEMS(type, count, static_var, dyn_var)	\
  type static_var[MAX_REQS];					\
  type* dyn_var = static_var;					\
  if ((count) > MAX_REQS)					\
    dyn_var = (type*)alloca(sizeof(type) * (count))

#define GET_ARRAY_ITEM(_base_addr, _size, _index) \
  (void*)(((uintptr_t)(_base_addr)) + ((_size)*(_index)))

/* convert a C request (ie. a pointer to a MPI_Request structure)
 * to an integer
 */
//#define EZTRACE_REQ(r) MPI_Request_c2f(*(r))

/* pointers to actual MPI functions (C version)  */
extern int (*libMPI_Init)(int*, char***);
extern int (*libMPI_Init_thread)(int*, char***, int, int*);
extern int (*libMPI_Comm_size)(MPI_Comm, int*);
extern int (*libMPI_Comm_rank)(MPI_Comm, int*);
extern int (*libMPI_Finalize)(void);
extern int (*libMPI_Initialized)(int*);
extern int (*libMPI_Abort)(MPI_Comm, int);

extern int (*libMPI_Cancel)(MPI_Request*);

extern int (*libMPI_Send)(CONST void* buf, int count, MPI_Datatype datatype,
                          int dest, int tag, MPI_Comm comm);
extern int (*libMPI_Recv)(void* buf, int count, MPI_Datatype datatype,
                          int source, int tag, MPI_Comm comm,
                          MPI_Status* status);

extern int (*libMPI_Bsend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm);
extern int (*libMPI_Ssend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm);
extern int (*libMPI_Rsend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm);
extern int (*libMPI_Isend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm,
                           MPI_Request*);
extern int (*libMPI_Ibsend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm,
                            MPI_Request*);
extern int (*libMPI_Issend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm,
                            MPI_Request*);
extern int (*libMPI_Irsend)(CONST void*, int, MPI_Datatype, int, int, MPI_Comm,
                            MPI_Request*);
extern int (*libMPI_Irecv)(void*, int, MPI_Datatype, int, int, MPI_Comm,
                           MPI_Request*);

extern int (*libMPI_Sendrecv)(CONST void*, int, MPI_Datatype, int, int, void*,
                              int, MPI_Datatype, int, int, MPI_Comm,
                              MPI_Status*);
extern int (*libMPI_Sendrecv_replace)(void*, int, MPI_Datatype, int, int, int,
                                      int, MPI_Comm, MPI_Status*);

extern int (*libMPI_Send_init)(CONST void*, int, MPI_Datatype, int, int,
                               MPI_Comm, MPI_Request*);
extern int (*libMPI_Bsend_init)(CONST void*, int, MPI_Datatype, int, int,
                                MPI_Comm, MPI_Request*);
extern int (*libMPI_Ssend_init)(CONST void*, int, MPI_Datatype, int, int,
                                MPI_Comm, MPI_Request*);
extern int (*libMPI_Rsend_init)(CONST void*, int, MPI_Datatype, int, int,
                                MPI_Comm, MPI_Request*);
extern int (*libMPI_Recv_init)(void*, int, MPI_Datatype, int, int, MPI_Comm,
                               MPI_Request*);
extern int (*libMPI_Start)(MPI_Request*);
extern int (*libMPI_Startall)(int, MPI_Request*);

extern int (*libMPI_Wait)(MPI_Request*, MPI_Status*);
extern int (*libMPI_Test)(MPI_Request*, int*, MPI_Status*);
extern int (*libMPI_Waitany)(int, MPI_Request*, int*, MPI_Status*);
extern int (*libMPI_Testany)(int, MPI_Request*, int*, int*, MPI_Status*);
extern int (*libMPI_Waitall)(int, MPI_Request*, MPI_Status*);
extern int (*libMPI_Testall)(int, MPI_Request*, int*, MPI_Status*);
extern int (*libMPI_Waitsome)(int, MPI_Request*, int*, int*, MPI_Status*);
extern int (*libMPI_Testsome)(int, MPI_Request*, int*, int*, MPI_Status*);

extern int (*libMPI_Probe)(int source, int tag, MPI_Comm comm,
                           MPI_Status* status);
extern int (*libMPI_Iprobe)(int source, int tag, MPI_Comm comm, int* flag,
                            MPI_Status* status);

extern int (*libMPI_Barrier)(MPI_Comm);
extern int (*libMPI_Bcast)(void*, int, MPI_Datatype, int, MPI_Comm);
extern int (*libMPI_Gather)(CONST void*, int, MPI_Datatype, void*, int,
                            MPI_Datatype, int, MPI_Comm);
extern int (*libMPI_Gatherv)(CONST void*, int, MPI_Datatype, void*, CONST int*,
                             CONST int*, MPI_Datatype, int, MPI_Comm);
extern int (*libMPI_Scatter)(CONST void*, int, MPI_Datatype, void*, int,
                             MPI_Datatype, int, MPI_Comm);
extern int (*libMPI_Scatterv)(CONST void*, CONST int*, CONST int*,
                              MPI_Datatype, void*, int, MPI_Datatype, int,
                              MPI_Comm);
extern int (*libMPI_Allgather)(CONST void*, int, MPI_Datatype, void*, int,
                               MPI_Datatype, MPI_Comm);
extern int (*libMPI_Allgatherv)(CONST void*, int, MPI_Datatype, void*,
                                CONST int*, CONST int*, MPI_Datatype,
                                MPI_Comm);
extern int (*libMPI_Alltoall)(CONST void*, int, MPI_Datatype, void*, int,
                              MPI_Datatype, MPI_Comm);
extern int (*libMPI_Alltoallv)(CONST void*, CONST int*, CONST int*,
                               MPI_Datatype, void*, CONST int*, CONST int*,
                               MPI_Datatype, MPI_Comm);
extern int (*libMPI_Reduce)(CONST void*, void*, int, MPI_Datatype, MPI_Op, int,
                            MPI_Comm);
extern int (*libMPI_Allreduce)(CONST void*, void*, int, MPI_Datatype, MPI_Op,
                               MPI_Comm);
extern int (*libMPI_Reduce_scatter)(CONST void*, void*, CONST int*,
                                    MPI_Datatype, MPI_Op, MPI_Comm);
extern int (*libMPI_Scan)(CONST void*, void*, int, MPI_Datatype, MPI_Op,
                          MPI_Comm);

#ifdef USE_MPI3
extern int (*libMPI_Ibarrier)(MPI_Comm, MPI_Request*);
extern int (*libMPI_Ibcast)(void*, int, MPI_Datatype, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Igather)(const void*, int, MPI_Datatype, void*, int, MPI_Datatype, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Igatherv)(const void*, int, MPI_Datatype, void*, const int*, const int*, MPI_Datatype, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iscatter)(const void*, int, MPI_Datatype, void*, int, MPI_Datatype, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iscatterv)(const void*, const int*, const int*, MPI_Datatype, void*, int, MPI_Datatype, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iallgather)(const void*, int, MPI_Datatype, void*, int, MPI_Datatype, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iallgatherv)(const void*, int, MPI_Datatype, void*, const int*, const int*, MPI_Datatype, MPI_Comm, MPI_Request*);
extern int (*libMPI_Ialltoall)(const void*, int, MPI_Datatype, void*, int, MPI_Datatype, MPI_Comm, MPI_Request*);
extern int (*libMPI_Ialltoallv)(const void*, const int*, const int*, MPI_Datatype, void*, const int*, const int*, MPI_Datatype, MPI_Comm, MPI_Request*);
extern int (*libMPI_Ireduce)(const void*, void*, int, MPI_Datatype, MPI_Op, int, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iallreduce)(const void*, void*, int, MPI_Datatype, MPI_Op, MPI_Comm, MPI_Request*);
extern int (*libMPI_Ireduce_scatter)(const void*, void*, const int*, MPI_Datatype, MPI_Op, MPI_Comm, MPI_Request*);
extern int (*libMPI_Iscan)(const void*, void*, int, MPI_Datatype, MPI_Op, MPI_Comm, MPI_Request*);
#endif

extern int (*libMPI_Get)(void*, int, MPI_Datatype, int, MPI_Aint, int,
                         MPI_Datatype, MPI_Win);
extern int (*libMPI_Put)(CONST void*, int, MPI_Datatype, int, MPI_Aint, int,
                         MPI_Datatype, MPI_Win);

extern int (*libMPI_Comm_spawn)(CONST char* command, char* argv[], int maxprocs,
                                MPI_Info info, int root, MPI_Comm comm,
                                MPI_Comm* intercomm, int array_of_errcodes[]);

/* fortran bindings */
extern void (*libmpi_init_)(int* e);
extern void (*libmpi_init_thread_)(int*, int*, int*);
extern void (*libmpi_finalize_)(int*);
extern void (*libmpi_barrier_)(MPI_Comm*, int*);
extern void (*libmpi_comm_size_)(MPI_Comm*, int*, int*);
extern void (*libmpi_comm_rank_)(MPI_Comm*, int*, int*);
extern void (*libmpi_cancel_)(MPI_Request*, int*);

extern void (*libmpi_send_)(void*, int*, MPI_Datatype*, int*, int*, int*);
extern void (*libmpi_recv_)(void*, int*, MPI_Datatype*, int*, int*,
                            MPI_Status*, int*);

extern void (*libmpi_sendrecv_)(void*, int, MPI_Datatype, int, int, void*,
                                int, MPI_Datatype, int, int, MPI_Comm,
                                MPI_Status*, int*);
extern void (*libmpi_sendrecv_replace_)(void*, int, MPI_Datatype, int, int, int,
                                        int, MPI_Comm, MPI_Status*, int*);

extern void (*libmpi_bsend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                             int*);
extern void (*libmpi_ssend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                             int*);
extern void (*libmpi_rsend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                             int*);
extern void (*libmpi_isend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                             MPI_Request*, int*);
extern void (*libmpi_ibsend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                              MPI_Request*, int*);
extern void (*libmpi_issend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                              MPI_Request*, int*);
extern void (*libmpi_irsend_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                              MPI_Request*, int*);
extern void (*libmpi_irecv_)(void*, int*, MPI_Datatype*, int*, int*, MPI_Comm*,
                             MPI_Request*, int*);

extern void (*libmpi_wait_)(MPI_Request*, MPI_Status*, int*);
extern void (*libmpi_test_)(MPI_Request*, int*, MPI_Status*, int*);
extern void (*libmpi_waitany_)(int*, MPI_Request*, int*, MPI_Status*, int*);
extern void (*libmpi_testany_)(int*, MPI_Request*, int*, int*, MPI_Status*,
                               int*);
extern void (*libmpi_waitall_)(int*, MPI_Request*, MPI_Status*, int*);
extern void (*libmpi_testall_)(int*, MPI_Request*, int*, MPI_Status*, int*);
extern void (*libmpi_waitsome_)(int*, MPI_Request*, int*, int*, MPI_Status*,
                                int*);
extern void (*libmpi_testsome_)(int*, MPI_Request*, int*, int*, MPI_Status*,
                                int*);

extern void (*libmpi_probe_)(int* source, int* tag, MPI_Comm* comm,
                             MPI_Status* status, int* err);
extern void (*libmpi_iprobe_)(int* source, int* tag, MPI_Comm* comm, int* flag,
                              MPI_Status* status, int* err);

extern void (*libmpi_get_)(void*, int*, MPI_Datatype*, int*, MPI_Aint*, int*,
                           MPI_Datatype*, MPI_Win*, int*);
extern void (*libmpi_put_)(void*, int*, MPI_Datatype*, int*, MPI_Aint*, int*,
                           MPI_Datatype*, MPI_Win*, int*);

extern void (*libmpi_bcast_)(void*, int*, MPI_Datatype*, int*, MPI_Comm*, int*);
extern void (*libmpi_gather_)(void*, int*, MPI_Datatype*, void*, int*,
                              MPI_Datatype*, int*, MPI_Comm*, int*);
extern void (*libmpi_gatherv_)(void*, int*, MPI_Datatype*, void*, int*, int*,
                               MPI_Datatype*, int*, MPI_Comm*);
extern void (*libmpi_scatter_)(void*, int*, MPI_Datatype*, void*, int*,
                               MPI_Datatype*, int*, MPI_Comm*, int*);
extern void (*libmpi_scatterv_)(void*, int*, int*, MPI_Datatype*, void*, int*,
                                MPI_Datatype*, int*, MPI_Comm*, int*);
extern void (*libmpi_allgather_)(void*, int*, MPI_Datatype*, void*, int*,
                                 MPI_Datatype*, MPI_Comm*, int*);
extern void (*libmpi_allgatherv_)(void*, int*, MPI_Datatype*, void*, int*, int*,
                                  MPI_Datatype*, MPI_Comm*);
extern void (*libmpi_alltoall_)(void*, int*, MPI_Datatype*, void*, int*,
                                MPI_Datatype*, MPI_Comm*, int*);
extern void (*libmpi_alltoallv_)(void*, int*, int*, MPI_Datatype*, void*, int*,
                                 int*, MPI_Datatype*, MPI_Comm*, int*);
extern void (*libmpi_reduce_)(void*, void*, int*, MPI_Datatype*, MPI_Op*, int*,
                              MPI_Comm*, int*);
extern void (*libmpi_allreduce_)(void*, void*, int*, MPI_Datatype*, MPI_Op*,
                                 MPI_Comm*, int*);
extern void (*libmpi_reduce_scatter_)(void*, void*, int*, MPI_Datatype*,
                                      MPI_Op*, MPI_Comm*, int*);
extern void (*libmpi_scan_)(void*, void*, int*, MPI_Datatype*, MPI_Op*,
                            MPI_Comm*, int*);

extern void (*libmpi_comm_spawn_)(char* command, char** argv, int* maxprocs,
                                  MPI_Info* info, int* root, MPI_Comm* comm,
                                  MPI_Comm* intercomm, int* array_of_errcodes,
                                  int* error);

extern void (*libmpi_send_init_)(void*, int*, MPI_Datatype*, int*, int*,
                                 MPI_Comm*, MPI_Request*, int*);
extern void (*libmpi_bsend_init_)(void*, int*, MPI_Datatype*, int*, int*,
                                  MPI_Comm*, MPI_Request*, int*);
extern void (*libmpi_ssend_init_)(void*, int*, MPI_Datatype*, int*, int*,
                                  MPI_Comm*, MPI_Request*, int*);
extern void (*libmpi_rsend_init_)(void*, int*, MPI_Datatype*, int*, int*,
                                  MPI_Comm*, MPI_Request*, int*);
extern void (*libmpi_recv_init_)(void*, int*, MPI_Datatype*, int*, int*,
                                 MPI_Comm*, MPI_Request*, int*);
extern void (*libmpi_start_)(MPI_Request*, int*);
extern void (*libmpi_startall_)(int*, MPI_Request*, int*);

/* return 1 if buf corresponds to the Fotran MPI_IN_PLACE
 * return 0 otherwise
 */
int ezt_mpi_is_in_place_(void* buf);

/* check the value of a Fortran pointer and return MPI_IN_PLACE or p
 */
#define CHECK_MPI_IN_PLACE(p) (ezt_mpi_is_in_place_(p) ? MPI_IN_PLACE : (p))

/* in some cases (eg on some architectures, or with some MPI implementations), some MPI datatypes
 * are not defined :/
 */
#ifdef MPI_INTEGER1
#define _EZT_MPI_INTEGER1 MPI_INTEGER1
#else
#define _EZT_MPI_INTEGER1 MPI_DATATYPE_NULL
#endif

#ifdef MPI_INTEGER2
#define _EZT_MPI_INTEGER2 MPI_INTEGER2
#else
#define _EZT_MPI_INTEGER2 MPI_DATATYPE_NULL
#endif

#ifdef MPI_INTEGER4
#define _EZT_MPI_INTEGER4 MPI_INTEGER4
#else
#define _EZT_MPI_INTEGER4 MPI_DATATYPE_NULL
#endif

#ifdef MPI_INTEGER8
#define _EZT_MPI_INTEGER8 MPI_INTEGER8
#else
#define _EZT_MPI_INTEGER8 MPI_DATATYPE_NULL
#endif

#ifdef MPI_INTEGER16
#define _EZT_MPI_INTEGER16 MPI_INTEGER16
#else
#define _EZT_MPI_INTEGER16 MPI_DATATYPE_NULL
#endif

#ifdef MPI_REAL4
#define _EZT_MPI_REAL4 MPI_REAL4
#else
#define _EZT_MPI_REAL4 MPI_DATATYPE_NULL
#endif

#ifdef MPI_REAL8
#define _EZT_MPI_REAL8 MPI_REAL8
#else
#define _EZT_MPI_REAL8 MPI_DATATYPE_NULL
#endif

#ifdef MPI_REAL16
#define _EZT_MPI_REAL16 MPI_REAL16
#else
#define _EZT_MPI_REAL16 MPI_DATATYPE_NULL
#endif

#ifdef MPI_COMPLEX8
#define _EZT_MPI_COMPLEX8 MPI_COMPLEX8
#else
#define _EZT_MPI_COMPLEX8 MPI_DATATYPE_NULL
#endif

#ifdef MPI_COMPLEX16
#define _EZT_MPI_COMPLEX16 MPI_COMPLEX16
#else
#define _EZT_MPI_COMPLEX16 MPI_DATATYPE_NULL
#endif

#ifdef MPI_COMPLEX32
#define _EZT_MPI_COMPLEX32 MPI_COMPLEX32
#else
#define _EZT_MPI_COMPLEX32 MPI_DATATYPE_NULL
#endif

#define EZT_DATATYPE_TO_MPI(ezt_datatype)				\
  ezt_datatype == EZT_MPI_DATATYPE_NULL ? MPI_DATATYPE_NULL:		\
    ezt_datatype == EZT_MPI_CHAR ? MPI_CHAR :				\
    ezt_datatype == EZT_MPI_UNSIGNED_CHAR ? MPI_UNSIGNED_CHAR :		\
    ezt_datatype == EZT_MPI_SHORT ? MPI_SHORT :				\
    ezt_datatype == EZT_MPI_UNSIGNED_SHORT ? MPI_UNSIGNED_SHORT :	\
    ezt_datatype == EZT_MPI_INT ? MPI_INT :				\
    ezt_datatype == EZT_MPI_UNSIGNED ? MPI_UNSIGNED :			\
    ezt_datatype == EZT_MPI_LONG ? MPI_LONG :				\
    ezt_datatype == EZT_MPI_UNSIGNED_LONG ? MPI_UNSIGNED_LONG :		\
    ezt_datatype == EZT_MPI_LONG_LONG_INT ? MPI_LONG_LONG_INT :		\
    ezt_datatype == EZT_MPI_LONG_LONG ? MPI_LONG_LONG :			\
    ezt_datatype == EZT_MPI_FLOAT ? MPI_FLOAT :				\
    ezt_datatype == EZT_MPI_DOUBLE ? MPI_DOUBLE :			\
    ezt_datatype == EZT_MPI_LONG_DOUBLE ? MPI_LONG_DOUBLE :		\
    ezt_datatype == EZT_MPI_BYTE ? MPI_BYTE :				\
    ezt_datatype == EZT_MPI_WCHAR ? MPI_WCHAR :				\
    ezt_datatype == EZT_MPI_PACKED ? MPI_PACKED :			\
    ezt_datatype == EZT_MPI_C_COMPLEX ? MPI_C_COMPLEX :			\
    ezt_datatype == EZT_MPI_C_FLOAT_COMPLEX ? MPI_C_FLOAT_COMPLEX :	\
    ezt_datatype == EZT_MPI_C_DOUBLE_COMPLEX ? MPI_C_DOUBLE_COMPLEX :	\
    ezt_datatype == EZT_MPI_C_LONG_DOUBLE_COMPLEX ? MPI_C_LONG_DOUBLE_COMPLEX : \
    ezt_datatype == EZT_MPI_2INT ? MPI_2INT :				\
    ezt_datatype == EZT_MPI_C_BOOL ? MPI_C_BOOL :			\
    ezt_datatype == EZT_MPI_SIGNED_CHAR ? MPI_SIGNED_CHAR :		\
    ezt_datatype == EZT_MPI_UNSIGNED_LONG_LONG ? MPI_UNSIGNED_LONG_LONG : \
    ezt_datatype == EZT_MPI_CHARACTER ? MPI_CHARACTER :			\
    ezt_datatype == EZT_MPI_INTEGER ? MPI_INTEGER :			\
    ezt_datatype == EZT_MPI_REAL ? MPI_REAL :				\
    ezt_datatype == EZT_MPI_LOGICAL ? MPI_LOGICAL :			\
    ezt_datatype == EZT_MPI_COMPLEX ? MPI_COMPLEX :			\
    ezt_datatype == EZT_MPI_DOUBLE_PRECISION ? MPI_DOUBLE_PRECISION :	\
    ezt_datatype == EZT_MPI_2INTEGER ? MPI_2INTEGER :			\
    ezt_datatype == EZT_MPI_2REAL ? MPI_2REAL :				\
    ezt_datatype == EZT_MPI_DOUBLE_COMPLEX ? MPI_DOUBLE_COMPLEX :	\
    ezt_datatype == EZT_MPI_2DOUBLE_PRECISION ? MPI_2DOUBLE_PRECISION : \
    ezt_datatype == EZT_MPI_REAL4 ? _EZT_MPI_REAL4 :			\
    ezt_datatype == EZT_MPI_COMPLEX8 ? _EZT_MPI_COMPLEX8 :		\
    ezt_datatype == EZT_MPI_REAL8 ? _EZT_MPI_REAL8 :			\
    ezt_datatype == EZT_MPI_COMPLEX16 ? _EZT_MPI_COMPLEX16 :		\
    ezt_datatype == EZT_MPI_REAL16 ? _EZT_MPI_REAL16 :			\
    ezt_datatype == EZT_MPI_COMPLEX32 ? _EZT_MPI_COMPLEX32 :		\
    ezt_datatype == EZT_MPI_INTEGER1 ? _EZT_MPI_INTEGER1 :		\
    ezt_datatype == EZT_MPI_INTEGER2 ? _EZT_MPI_INTEGER2 :		\
    ezt_datatype == EZT_MPI_INTEGER4 ? _EZT_MPI_INTEGER4 :		\
    ezt_datatype == EZT_MPI_INTEGER8 ? _EZT_MPI_INTEGER8 :		\
    ezt_datatype == EZT_MPI_INTEGER16 ? _EZT_MPI_INTEGER16 :		\
    ezt_datatype == EZT_MPI_INT8_T ? MPI_INT8_T :			\
    ezt_datatype == EZT_MPI_INT16_T ? MPI_INT16_T :			\
    ezt_datatype == EZT_MPI_INT32_T ? MPI_INT32_T :			\
    ezt_datatype == EZT_MPI_INT64_T ? MPI_INT64_T :			\
    ezt_datatype == EZT_MPI_UINT8_T ? MPI_UINT8_T :			\
    ezt_datatype == EZT_MPI_UINT16_T ? MPI_UINT16_T :			\
    ezt_datatype == EZT_MPI_UINT32_T ? MPI_UINT32_T :			\
    ezt_datatype == EZT_MPI_UINT64_T ? MPI_UINT64_T :			\
    ezt_datatype == EZT_MPI_AINT ? MPI_AINT :				\
    ezt_datatype == EZT_MPI_OFFSET ? MPI_OFFSET :			\
    ezt_datatype == EZT_MPI_FLOAT_INT ? MPI_FLOAT_INT :			\
    ezt_datatype == EZT_MPI_DOUBLE_INT ? MPI_DOUBLE_INT :		\
    ezt_datatype == EZT_MPI_LONG_INT ? MPI_LONG_INT :			\
    ezt_datatype == EZT_MPI_SHORT_INT ? MPI_SHORT_INT :			\
    ezt_datatype == EZT_MPI_LONG_DOUBLE_INT ? MPI_LONG_DOUBLE_INT :	\
    EZT_MPI_DATATYPE_NULL

#define EZT_OP_TO_MPI(ezt_op)			\
  ezt_op == EZT_MPI_MAX ? MPI_MAX :		\
    ezt_op == EZT_MPI_MIN ? MPI_MIN :		\
    ezt_op == EZT_MPI_SUM ? MPI_SUM :		\
    ezt_op == EZT_MPI_PROD ? MPI_PROD :		\
    ezt_op == EZT_MPI_LAND ? MPI_LAND :		\
    ezt_op == EZT_MPI_BAND ? MPI_BAND :		\
    ezt_op == EZT_MPI_LOR ? MPI_LOR :		\
    ezt_op == EZT_MPI_BOR ? MPI_BOR :		\
    ezt_op == EZT_MPI_LXOR ? MPI_LXOR :		\
    ezt_op == EZT_MPI_BXOR ? MPI_BXOR :		\
    ezt_op == EZT_MPI_MAXLOC ? MPI_MAXLOC :	\
    MPI_MINLOC
    
extern OTF2_CommRef comm_world_ref;

OTF2_CommRef MPI_TO_OTF_COMMUNICATOR(MPI_Comm comm __attribute__((unused)));

enum mpi_request_type {	/* P2P operations*/
		       recv,
		       send,
		       bsend,
		       rsend,
		       ssend,
		       /* Collective operations */
		       ibarrier, 
		       ibcast, 
		       igather, 
		       igatherv, 
		       iscatter, 
		       iscatterv, 
		       iallgather, 
		       iallgatherv, 
		       ialltoall, 
		       ialltoallv, 
		       ialltoallw, 
		       iallreduce, 
		       ireduce, 
		       ireduce_scatter, 
};

struct ezt_mpi_request {
  MPI_Request *req;
  enum mpi_request_type type;
  MPI_Comm comm;
  /* only used for collective: */
  int root;
  int send_size;
  int recv_size;
  /* only used for persistent */
  int dest;
  int tag;
  int len;
  int persistent;
};

struct ezt_mpi_request* ezt_mpi_get_request_type(MPI_Request *req, int persistent);
void ezt_mpi_set_request_type(MPI_Request *req, enum mpi_request_type type,
			      MPI_Comm comm, int root, int send_size, int recv_size);
void ezt_mpi_set_persistent_request_type(MPI_Request* req, enum mpi_request_type type,
					 MPI_Comm comm,
					 int dest, int tag, int len);

void mpi_complete_request(MPI_Fint* req,
			  MPI_Status* s);

// if a datatype is MPI_DATATYPE_NULL, calling MPI_Type_size is invalid and some MPI
// implementations (eg OpenMPI) generate an error
#define _EZT_MPI_Type_size(_type, _size) do {	\
    if((void*)_size != NULL) *(_size) = 0;	\
    if( (_type) != MPI_DATATYPE_NULL )		\
      MPI_Type_size(_type, _size);		\
  } while(0)

#endif /* MPI_EZTRACE_H */
