/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *
 *  (C) 2001 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

#include "mpiimpl.h"

/* MINLOC and MAXLOC structures */
typedef struct MPIR_2int_loctype {
    int value;
    int loc;
} MPIR_2int_loctype;

typedef struct MPIR_floatint_loctype {
    float value;
    int loc;
} MPIR_floatint_loctype;

typedef struct MPIR_longint_loctype {
    long value;
    int loc;
} MPIR_longint_loctype;

typedef struct MPIR_shortint_loctype {
    short value;
    int loc;
} MPIR_shortint_loctype;

typedef struct MPIR_doubleint_loctype {
    double value;
    int loc;
} MPIR_doubleint_loctype;

#if defined(HAVE_LONG_DOUBLE)
typedef struct MPIR_longdoubleint_loctype {
    long double value;
    int loc;
} MPIR_longdoubleint_loctype;
#endif

/* Note a subtlety in these two macros which avoids compiler warnings.
   The compiler complains about using == on floats, but the standard
   requires that we set loc to min of the locs if the two values are
   equal.  So we do "if a<b {} else if a<=b Y" which is the same as
   "if a<b X else if a==b Y" but avoids the warning. */
#define MPIR_MAXLOC_C_CASE(c_type_) {                   \
        c_type_ *a = (c_type_ *)inoutvec;               \
        c_type_ *b = (c_type_ *)invec;                  \
        for (i=0; i<len; i++) {                         \
            if (a[i].value < b[i].value) {              \
                a[i].value = b[i].value;                \
                a[i].loc   = b[i].loc;                  \
            } else if (a[i].value <= b[i].value)        \
                a[i].loc = MPL_MIN(a[i].loc,b[i].loc); \
        }                                               \
    }                                                   \
    break

#define MPIR_MAXLOC_F_CASE(f_type_) {                   \
        f_type_ *a = (f_type_ *)inoutvec;               \
        f_type_ *b = (f_type_ *)invec;                  \
        for (i=0; i<flen; i+=2) {                     \
            if (a[i] < b[i]) {                          \
                a[i]   = b[i];                          \
                a[i+1] = b[i+1];                        \
            } else if (a[i] <= b[i])                    \
                a[i+1] = MPL_MIN(a[i+1],b[i+1]);       \
        }                                               \
    }                                                   \
    break


#undef FUNCNAME
#define FUNCNAME MPIR_MAXLOC
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
void MPIR_MAXLOC(void *invec, void *inoutvec, int *Len, MPI_Datatype * type)
{
    int mpi_errno = MPI_SUCCESS;
    int i, len = *Len;

#ifdef HAVE_FORTRAN_BINDING
#ifndef HAVE_NO_FORTRAN_MPI_TYPES_IN_C
    int flen = len * 2;         /* used for Fortran types */
#endif
#endif

    switch (*type) {
            /* first the C types */
        case MPI_2INT:
            MPIR_MAXLOC_C_CASE(MPIR_2int_loctype);
        case MPI_FLOAT_INT:
            MPIR_MAXLOC_C_CASE(MPIR_floatint_loctype);
        case MPI_LONG_INT:
            MPIR_MAXLOC_C_CASE(MPIR_longint_loctype);
        case MPI_SHORT_INT:
            MPIR_MAXLOC_C_CASE(MPIR_shortint_loctype);
        case MPI_DOUBLE_INT:
            MPIR_MAXLOC_C_CASE(MPIR_doubleint_loctype);
#if defined(HAVE_LONG_DOUBLE)
        case MPI_LONG_DOUBLE_INT:
            MPIR_MAXLOC_C_CASE(MPIR_longdoubleint_loctype);
#endif

            /* now the Fortran types */
#ifdef HAVE_FORTRAN_BINDING
#ifndef HAVE_NO_FORTRAN_MPI_TYPES_IN_C
        case MPI_2INTEGER:
            MPIR_MAXLOC_F_CASE(MPI_Fint);
        case MPI_2REAL:
            MPIR_MAXLOC_F_CASE(MPIR_FC_REAL_CTYPE);
        case MPI_2DOUBLE_PRECISION:
            MPIR_MAXLOC_F_CASE(MPIR_FC_DOUBLE_CTYPE);
#endif
#endif
            /* --BEGIN ERROR HANDLING-- */
        default:{
                MPIR_ERR_SET1(mpi_errno, MPI_ERR_OP, "**opundefined", "**opundefined %s",
                              "MPI_MAXLOC");
                {
                    MPIR_Per_thread_t *per_thread = NULL;
                    int err = 0;

                    MPID_THREADPRIV_KEY_GET_ADDR(MPIR_ThreadInfo.isThreaded, MPIR_Per_thread_key,
                                                 MPIR_Per_thread, per_thread, &err);
                    MPIR_Assert(err == 0);
                    per_thread->op_errno = mpi_errno;
                }
                break;
            }
            /* --END ERROR HANDLING-- */
    }

}


#undef FUNCNAME
#define FUNCNAME MPIR_MAXLOC_check_dtype
#undef FCNAME
#define FCNAME MPL_QUOTE(FUNCNAME)
int MPIR_MAXLOC_check_dtype(MPI_Datatype type)
{
    int mpi_errno = MPI_SUCCESS;

    switch (type) {
            /* first the C types */
        case MPI_2INT:
        case MPI_FLOAT_INT:
        case MPI_LONG_INT:
        case MPI_SHORT_INT:
        case MPI_DOUBLE_INT:
#if defined(HAVE_LONG_DOUBLE)
        case MPI_LONG_DOUBLE_INT:
#endif
            /* now the Fortran types */
#ifdef HAVE_FORTRAN_BINDING
#ifndef HAVE_NO_FORTRAN_MPI_TYPES_IN_C
        case MPI_2INTEGER:
        case MPI_2REAL:
        case MPI_2DOUBLE_PRECISION:
#endif
#endif
            break;

        default:
            MPIR_ERR_SET1(mpi_errno, MPI_ERR_OP, "**opundefined", "**opundefined %s", "MPI_MAXLOC");
    }

    return mpi_errno;
}
