/* -*- 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 <stdlib.h>
#include <stdint.h>
#include "mpi.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

/* 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*) malloc(sizeof(type)*(count))

/* Free an array created by ALLOCATE_ITEMS */
#define FREE_ITEMS(count, dyn_var)		\
  if((count) > MAX_REQS)			\
    free(dyn_var)

/* 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))

#endif	/* MPI_EZTRACE_H */
