/*
 * Copyright (C) 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


void MPIR_MAXLOC(void *invec, void *inoutvec, int *Len, MPI_Datatype * type)
{
    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
        default:
            MPIR_Assert(0);
            break;
    }

}


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;
}
