/* -*- Mode: C; c-basic-offset:4 ; -*- */
/*
 * Copyright (c) 2004-2006 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2007 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2007 High Performance Computing Center Stuttgart, 
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2007-2009 Cisco Systems, Inc.  All rights reserved.
 * Copyright (c) 2009      Sun Microsystems, Inc.  All rights reserved.
 * $COPYRIGHT$
 * 
 * Additional copyrights may follow
 * 
 * $HEADER$
 */

#include "ompi_config.h"

#include "ompi/constants.h"
#include "ompi/op/op.h"
#include "ompi/op/op_predefined.h"
#include "opal/class/opal_pointer_array.h"
#include "ompi/datatype/datatype_internal.h"


/*
 * Table for Fortran <-> C op handle conversion
 */
opal_pointer_array_t *ompi_op_f_to_c_table;


/*
 * Create intrinsic op
 */
static int add_intrinsic(ompi_op_t *op, int fort_handle);


/*
 * Class information
 */
static void ompi_op_construct(ompi_op_t *eh);
static void ompi_op_destruct(ompi_op_t *eh);


/*
 * Class instance
 */
OBJ_CLASS_INSTANCE(ompi_op_t, opal_object_t, 
                   ompi_op_construct, ompi_op_destruct);


/*
 * Helpful defines, because there's soooo many names!
 *
 * **NOTE** These #define's are strictly ordered!  A series of macros
 * are built up to assemble a list of function names (or NULLs) that
 * are put into the intrinsict ompi_op_t's in the middle of this
 * file.  The order of these function names is critical, and must be
 * the same as the OMPI_OP_TYPE_* enums in op.h (i.e., the enum's
 * starting with OMPI_OP_TYPE_UNSIGNED_CHAR -- at the time of this writing,
 * this is op.h:78).
 */

/** C integer ***********************************************************/

#ifdef HAVE_LONG_LONG
#define C_INTEGER_LONG_LONG(name) \
  { ompi_mpi_op_##name##_long_long_int },  /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { ompi_mpi_op_##name##_unsigned_long_long } /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */
#define C_INTEGER_LONG_LONG_3BUFF(name) \
  { ompi_mpi_op_three_buff_##name##_long_long_int },  /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { ompi_mpi_op_three_buff_##name##_unsigned_long_long } /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */
#else
#define C_INTEGER_LONG_LONG(name) \
  { NULL }, /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { NULL }  /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */
#define C_INTEGER_LONG_LONG_3BUFF(name) \
  { NULL }, /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { NULL }  /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */
#endif

#define C_INTEGER(name) \
  { ompi_mpi_op_##name##_unsigned_char },  /* OMPI_OP_TYPE_UNSIGNED_CHAR */ \
  { ompi_mpi_op_##name##_signed_char },    /* OMPI_OP_TYPE_SIGNED_CHAR */ \
  { ompi_mpi_op_##name##_int },            /* OMPI_OP_TYPE_INT */ \
  { ompi_mpi_op_##name##_long },           /* OMPI_OP_TYPE_LONG */ \
  { ompi_mpi_op_##name##_short },          /* OMPI_OP_TYPE_SHORT */ \
  { ompi_mpi_op_##name##_unsigned_short }, /* OMPI_OP_TYPE_UNSIGNED_SHORT */ \
  { ompi_mpi_op_##name##_unsigned },       /* OMPI_OP_TYPE_UNSIGNED */ \
  { ompi_mpi_op_##name##_unsigned_long },  /* OMPI_OP_TYPE_UNSIGNED_LONG */ \
  C_INTEGER_LONG_LONG(name)
#define C_INTEGER_3BUFF(name) \
  { ompi_mpi_op_three_buff_##name##_unsigned_char },  /* OMPI_OP_TYPE_UNSIGNED_CHAR */ \
  { ompi_mpi_op_three_buff_##name##_signed_char },    /* OMPI_OP_TYPE_SIGNED_CHAR */ \
  { ompi_mpi_op_three_buff_##name##_int },            /* OMPI_OP_TYPE_INT */ \
  { ompi_mpi_op_three_buff_##name##_long },           /* OMPI_OP_TYPE_LONG */ \
  { ompi_mpi_op_three_buff_##name##_short },          /* OMPI_OP_TYPE_SHORT */ \
  { ompi_mpi_op_three_buff_##name##_unsigned_short }, /* OMPI_OP_TYPE_UNSIGNED_SHORT */ \
  { ompi_mpi_op_three_buff_##name##_unsigned },       /* OMPI_OP_TYPE_UNSIGNED */ \
  { ompi_mpi_op_three_buff_##name##_unsigned_long },  /* OMPI_OP_TYPE_UNSIGNED_LONG */ \
  C_INTEGER_LONG_LONG_3BUFF(name)

#define C_INTEGER_NULL \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_CHAR */ \
  { NULL }, /* OMPI_OP_TYPE_SIGNED_CHAR */ \
  { NULL }, /* OMPI_OP_TYPE_INT */ \
  { NULL }, /* OMPI_OP_TYPE_LONG */ \
  { NULL }, /* OMPI_OP_TYPE_SHORT */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_SHORT */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_LONG */ \
  { NULL }, /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { NULL }  /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */

#define C_INTEGER_NULL_3BUFF \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_CHAR */ \
  { NULL }, /* OMPI_OP_TYPE_SIGNED_CHAR */ \
  { NULL }, /* OMPI_OP_TYPE_INT */ \
  { NULL }, /* OMPI_OP_TYPE_LONG */ \
  { NULL }, /* OMPI_OP_TYPE_SHORT */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_SHORT */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED */ \
  { NULL }, /* OMPI_OP_TYPE_UNSIGNED_LONG */ \
  { NULL }, /* OMPI_OP_TYPE_LONG_LONG_INT */ \
  { NULL }  /* OMPI_OP_TYPE_UNSIGNED_LONG_LONG */

/** All the Fortran integers ********************************************/

#if OMPI_HAVE_FORTRAN_INTEGER
#define FORTRAN_INTEGER_PLAIN(name) { ompi_mpi_op_##name##_fortran_integer }
#define FORTRAN_INTEGER_PLAIN_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer }
#else
#define FORTRAN_INTEGER_PLAIN(name) { NULL }
#define FORTRAN_INTEGER_PLAIN_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER1
#define FORTRAN_INTEGER1(name) { ompi_mpi_op_##name##_fortran_integer1 }
#define FORTRAN_INTEGER1_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer1 }
#else
#define FORTRAN_INTEGER1(name) { NULL }
#define FORTRAN_INTEGER1_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER2
#define FORTRAN_INTEGER2(name) { ompi_mpi_op_##name##_fortran_integer2 }
#define FORTRAN_INTEGER2_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer2 }
#else
#define FORTRAN_INTEGER2(name) { NULL }
#define FORTRAN_INTEGER2_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER4
#define FORTRAN_INTEGER4(name) { ompi_mpi_op_##name##_fortran_integer4 }
#define FORTRAN_INTEGER4_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer4 }
#else
#define FORTRAN_INTEGER4(name) { NULL }
#define FORTRAN_INTEGER4_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER8
#define FORTRAN_INTEGER8(name) { ompi_mpi_op_##name##_fortran_integer8 }
#define FORTRAN_INTEGER8_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer8 }
#else
#define FORTRAN_INTEGER8(name) { NULL }
#define FORTRAN_INTEGER8_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER16
#define FORTRAN_INTEGER16(name) { ompi_mpi_op_##name##_fortran_integer16 }
#define FORTRAN_INTEGER16_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_integer16 }
#else
#define FORTRAN_INTEGER16(name) { NULL }
#define FORTRAN_INTEGER16_3BUFF(name) { NULL }
#endif
#define FORTRAN_INTEGER(name) \
  FORTRAN_INTEGER_PLAIN(name),      /* OMPI_OP_TYPE_INTEGER */ \
  FORTRAN_INTEGER1(name),           /* OMPI_OP_TYPE_INTEGER1 */ \
  FORTRAN_INTEGER2(name),           /* OMPI_OP_TYPE_INTEGER2 */ \
  FORTRAN_INTEGER4(name),           /* OMPI_OP_TYPE_INTEGER4 */ \
  FORTRAN_INTEGER8(name),           /* OMPI_OP_TYPE_INTEGER8 */ \
  FORTRAN_INTEGER16(name)           /* OMPI_OP_TYPE_INTEGER16 */

#define FORTRAN_INTEGER_3BUFF(name) \
  FORTRAN_INTEGER_PLAIN_3BUFF(name),      /* OMPI_OP_TYPE_INTEGER */ \
  FORTRAN_INTEGER1_3BUFF(name),           /* OMPI_OP_TYPE_INTEGER1 */ \
  FORTRAN_INTEGER2_3BUFF(name),           /* OMPI_OP_TYPE_INTEGER2 */ \
  FORTRAN_INTEGER4_3BUFF(name),           /* OMPI_OP_TYPE_INTEGER4 */ \
  FORTRAN_INTEGER8_3BUFF(name),           /* OMPI_OP_TYPE_INTEGER8 */ \
  FORTRAN_INTEGER16_3BUFF(name)           /* OMPI_OP_TYPE_INTEGER16 */

#define FORTRAN_INTEGER_NULL \
  { NULL },  /* OMPI_OP_TYPE_INTEGER */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER1 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER2 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER4 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER8 */ \
  { NULL }  /* OMPI_OP_TYPE_INTEGER16 */

#define FORTRAN_INTEGER_NULL_3BUFF \
  { NULL },  /* OMPI_OP_TYPE_INTEGER */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER1 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER2 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER4 */ \
  { NULL },  /* OMPI_OP_TYPE_INTEGER8 */ \
  { NULL }  /* OMPI_OP_TYPE_INTEGER16 */

/** All the Fortran reals ***********************************************/

#if OMPI_HAVE_FORTRAN_REAL
#define FLOATING_POINT_FORTRAN_REAL_PLAIN(name) { ompi_mpi_op_##name##_fortran_real }
#define FLOATING_POINT_FORTRAN_REAL_PLAIN_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_real }
#else
#define FLOATING_POINT_FORTRAN_REAL_PLAIN(name) { NULL }
#define FLOATING_POINT_FORTRAN_REAL_PLAIN_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_REAL2
#define FLOATING_POINT_FORTRAN_REAL2(name) { ompi_mpi_op_##name##_fortran_real2 }
#define FLOATING_POINT_FORTRAN_REAL2_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_real2 }
#else
#define FLOATING_POINT_FORTRAN_REAL2(name) { NULL }
#define FLOATING_POINT_FORTRAN_REAL2_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_REAL4
#define FLOATING_POINT_FORTRAN_REAL4(name) { ompi_mpi_op_##name##_fortran_real4 }
#define FLOATING_POINT_FORTRAN_REAL4_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_real4 }
#else
#define FLOATING_POINT_FORTRAN_REAL4(name) { NULL }
#define FLOATING_POINT_FORTRAN_REAL4_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_REAL8
#define FLOATING_POINT_FORTRAN_REAL8(name) { ompi_mpi_op_##name##_fortran_real8 }
#define FLOATING_POINT_FORTRAN_REAL8_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_real8 }
#else
#define FLOATING_POINT_FORTRAN_REAL8(name) { NULL }
#define FLOATING_POINT_FORTRAN_REAL8_3BUFF(name) { NULL }
#endif
/* If:
   - we have fortran REAL*16, *and*
   - fortran REAL*16 matches the bit representation of the
     corresponding C type
   Only then do we put in function pointers for REAL*16 reductions.
   Otherwise, just put in NULL. */
#if OMPI_HAVE_FORTRAN_REAL16 && OMPI_REAL16_MATCHES_C
#define FLOATING_POINT_FORTRAN_REAL16(name) { ompi_mpi_op_##name##_fortran_real16 }
#define FLOATING_POINT_FORTRAN_REAL16_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_real16 }
#else
#define FLOATING_POINT_FORTRAN_REAL16(name) { NULL }
#define FLOATING_POINT_FORTRAN_REAL16_3BUFF(name) { NULL }
#endif

#define FLOATING_POINT_FORTRAN_REAL(name) \
  FLOATING_POINT_FORTRAN_REAL_PLAIN(name),      /* OMPI_OP_TYPE_REAL */ \
  FLOATING_POINT_FORTRAN_REAL2(name),           /* OMPI_OP_TYPE_REAL2 */ \
  FLOATING_POINT_FORTRAN_REAL4(name),           /* OMPI_OP_TYPE_REAL4 */ \
  FLOATING_POINT_FORTRAN_REAL8(name),           /* OMPI_OP_TYPE_REAL8 */ \
  FLOATING_POINT_FORTRAN_REAL16(name)           /* OMPI_OP_TYPE_REAL16 */

#define FLOATING_POINT_FORTRAN_REAL_3BUFF(name) \
  FLOATING_POINT_FORTRAN_REAL_PLAIN_3BUFF(name),      /* OMPI_OP_TYPE_REAL */ \
  FLOATING_POINT_FORTRAN_REAL2_3BUFF(name),           /* OMPI_OP_TYPE_REAL2 */ \
  FLOATING_POINT_FORTRAN_REAL4_3BUFF(name),           /* OMPI_OP_TYPE_REAL4 */ \
  FLOATING_POINT_FORTRAN_REAL8_3BUFF(name),           /* OMPI_OP_TYPE_REAL8 */ \
  FLOATING_POINT_FORTRAN_REAL16_3BUFF(name)           /* OMPI_OP_TYPE_REAL16 */

/** Fortran double precision ********************************************/

#if OMPI_HAVE_FORTRAN_DOUBLE_PRECISION
#define FLOATING_POINT_FORTRAN_DOUBLE_PRECISION(name) \
    { ompi_mpi_op_##name##_fortran_double_precision }
#define FLOATING_POINT_FORTRAN_DOUBLE_PRECISION_3BUFF(name) \
    { ompi_mpi_op_three_buff_##name##_fortran_double_precision }
#else
#define FLOATING_POINT_FORTRAN_DOUBLE_PRECISION(name) { NULL }
#define FLOATING_POINT_FORTRAN_DOUBLE_PRECISION_3BUFF(name) { NULL }
#endif

/** Floating point, including all the Fortran reals *********************/

#define FLOATING_POINT(name) \
  { ompi_mpi_op_##name##_float },                    /* OMPI_OP_TYPE_FLOAT */\
  { ompi_mpi_op_##name##_double },                   /* OMPI_OP_TYPE_DOUBLE */\
  FLOATING_POINT_FORTRAN_REAL(name),                 /* OMPI_OP_TYPE_REAL */ \
  FLOATING_POINT_FORTRAN_DOUBLE_PRECISION(name),     /* OMPI_OP_TYPE_DOUBLE_PRECISION */ \
  { ompi_mpi_op_##name##_long_double }               /* OMPI_OP_TYPE_LONG_DOUBLE */

#define FLOATING_POINT_3BUFF(name) \
  { ompi_mpi_op_three_buff_##name##_float },                    /* OMPI_OP_TYPE_FLOAT */\
  { ompi_mpi_op_three_buff_##name##_double },                   /* OMPI_OP_TYPE_DOUBLE */\
  FLOATING_POINT_FORTRAN_REAL_3BUFF(name),                 /* OMPI_OP_TYPE_REAL */ \
  FLOATING_POINT_FORTRAN_DOUBLE_PRECISION_3BUFF(name),     /* OMPI_OP_TYPE_DOUBLE_PRECISION */ \
  { ompi_mpi_op_three_buff_##name##_long_double }               /* OMPI_OP_TYPE_LONG_DOUBLE */

#define FLOATING_POINT_NULL \
  { NULL }, /* OMPI_OP_TYPE_FLOAT */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE */ \
  { NULL }, /* OMPI_OP_TYPE_REAL */ \
  { NULL }, /* OMPI_OP_TYPE_REAL2 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL4 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL8 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL16 */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE_PRECISION */ \
  { NULL }  /* OMPI_OP_TYPE_LONG_DOUBLE */

#define FLOATING_POINT_NULL_3BUFF \
  { NULL }, /* OMPI_OP_TYPE_FLOAT */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE */ \
  { NULL }, /* OMPI_OP_TYPE_REAL */ \
  { NULL }, /* OMPI_OP_TYPE_REAL2 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL4 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL8 */ \
  { NULL }, /* OMPI_OP_TYPE_REAL16 */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE_PRECISION */ \
  { NULL }  /* OMPI_OP_TYPE_LONG_DOUBLE */

/** Fortran logical *****************************************************/

#if OMPI_HAVE_FORTRAN_LOGICAL
#define FORTRAN_LOGICAL(name) \
  { ompi_mpi_op_##name##_fortran_logical }  /* OMPI_OP_TYPE_LOGICAL */
#define FORTRAN_LOGICAL_3BUFF(name) \
  { ompi_mpi_op_three_buff_##name##_fortran_logical }  /* OMPI_OP_TYPE_LOGICAL */
#else
#define FORTRAN_LOGICAL(name) { NULL }
#define FORTRAN_LOGICAL_3BUFF(name) { NULL }
#endif
#define LOGICAL(name) \
  FORTRAN_LOGICAL(name), \
  { ompi_mpi_op_##name##_bool }  /* OMPI_OP_TYPE_BOOL */
#define LOGICAL_3BUFF(name) \
  FORTRAN_LOGICAL_3BUFF(name), \
  { ompi_mpi_op_three_buff_##name##_bool }  /* OMPI_OP_TYPE_BOOL */

#define LOGICAL_NULL \
  { NULL },  /* OMPI_OP_TYPE_LOGICAL */ \
  { NULL }   /* OMPI_OP_TYPE_BOOL */

#define LOGICAL_NULL_3BUFF \
  { NULL },  /* OMPI_OP_TYPE_LOGICAL */ \
  { NULL }   /* OMPI_OP_TYPE_BOOL */

/** Fortran complex *****************************************************/

#if OMPI_HAVE_FORTRAN_REAL && OMPI_HAVE_FORTRAN_COMPLEX
#define COMPLEX_PLAIN(name) { ompi_mpi_op_##name##_fortran_complex }
#define COMPLEX_PLAIN_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_complex }
#else
#define COMPLEX_PLAIN(name) { NULL }
#define COMPLEX_PLAIN_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_DOUBLE_PRECISION && OMPI_HAVE_FORTRAN_COMPLEX
#define COMPLEX_DOUBLE(name) { ompi_mpi_op_##name##_fortran_double_complex }
#define COMPLEX_DOUBLE_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_double_complex }
#else
#define COMPLEX_DOUBLE(name) { NULL }
#define COMPLEX_DOUBLE_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_REAL4 && OMPI_HAVE_FORTRAN_COMPLEX8
#define COMPLEX8(name) { ompi_mpi_op_##name##_fortran_complex8 }
#define COMPLEX8_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_complex8 }
#else
#define COMPLEX8(name) { NULL }
#define COMPLEX8_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_REAL8 && OMPI_HAVE_FORTRAN_COMPLEX16
#define COMPLEX16(name) { ompi_mpi_op_##name##_fortran_complex16 }
#define COMPLEX16_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_complex16 }
#else
#define COMPLEX16(name) { NULL }
#define COMPLEX16_3BUFF(name) { NULL }
#endif
/* If:
   - we have fortran REAL*16, *and*
   - fortran REAL*16 matches the bit representation of the
     corresponding C type, *and*
   - we have fortran COMPILEX*32
   Only then do we put in function pointers for COMPLEX*32 reductions.
   Otherwise, just put in NULL. */
#if OMPI_HAVE_FORTRAN_REAL16 && OMPI_REAL16_MATCHES_C && OMPI_HAVE_FORTRAN_COMPLEX32
#define COMPLEX32(name) { ompi_mpi_op_##name##_fortran_complex32 }
#define COMPLEX32_3BUFF(name) { ompi_mpi_op_three_buff_##name##_fortran_complex32 }
#else
#define COMPLEX32(name) { NULL }
#define COMPLEX32_3BUFF(name) { NULL }
#endif

#define COMPLEX(name) \
  COMPLEX_PLAIN(name),  /* OMPI_OP_TYPE_COMPLEX */ \
  COMPLEX_DOUBLE(name), /* OMPI_OP_TYPE_DOUBLE_COMPLEX */ \
  COMPLEX8(name),       /* OMPI_OP_TYPE_COMPLEX8 */ \
  COMPLEX16(name),      /* OMPI_OP_TYPE_COMPLEX16 */ \
  COMPLEX32(name)       /* OMPI_OP_TYPE_COMPLEX32 */

#define COMPLEX_3BUFF(name) \
  COMPLEX_PLAIN_3BUFF(name),  /* OMPI_OP_TYPE_COMPLEX */ \
  COMPLEX_DOUBLE_3BUFF(name), /* OMPI_OP_TYPE_DOUBLE_COMPLEX */ \
  COMPLEX8_3BUFF(name),       /* OMPI_OP_TYPE_COMPLEX8 */ \
  COMPLEX16_3BUFF(name),      /* OMPI_OP_TYPE_COMPLEX16 */ \
  COMPLEX32_3BUFF(name)       /* OMPI_OP_TYPE_COMPLEX32 */

#define COMPLEX_NULL \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX */ \
  { NULL },  /* OMPI_OP_TYPE_DOUBLE_COMPLEX */ \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX8 */ \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX16 */ \
  { NULL }   /* OMPI_OP_TYPE_COMPLEX32 */

#define COMPLEX_NULL_3BUFF \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX */ \
  { NULL },  /* OMPI_OP_TYPE_DOUBLE_COMPLEX */ \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX8 */ \
  { NULL },  /* OMPI_OP_TYPE_COMPLEX16 */ \
  { NULL }   /* OMPI_OP_TYPE_COMPLEX32 */

/** Byte ****************************************************************/

#define BYTE(name) \
  { ompi_mpi_op_##name##_byte }  /* OMPI_OP_TYPE_BYTE */
#define BYTE_3BUFF(name) \
  { ompi_mpi_op_three_buff_##name##_byte }  /* OMPI_OP_TYPE_BYTE */

#define BYTE_NULL \
  { NULL }  /* OMPI_OP_TYPE_BYTE */

#define BYTE_NULL_3BUFF \
  { NULL }  /* OMPI_OP_TYPE_BYTE */

/** Fortran complex *****************************************************/
/** Fortran "2" types ***************************************************/

#if OMPI_HAVE_FORTRAN_REAL
#define TWOLOC_FORTRAN_2REAL(name) { ompi_mpi_op_##name##_2real }
#define TWOLOC_FORTRAN_2REAL_3BUFF(name) { ompi_mpi_op_three_buff_##name##_2real }
#else
#define TWOLOC_FORTRAN_2REAL(name) { NULL }
#define TWOLOC_FORTRAN_2REAL_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_DOUBLE_PRECISION
#define TWOLOC_FORTRAN_2DOUBLE_PRECISION(name) { ompi_mpi_op_##name##_2double_precision }
#define TWOLOC_FORTRAN_2DOUBLE_PRECISION_3BUFF(name) { ompi_mpi_op_three_buff_##name##_2double_precision }
#else
#define TWOLOC_FORTRAN_2DOUBLE_PRECISION(name) { NULL }
#define TWOLOC_FORTRAN_2DOUBLE_PRECISION_3BUFF(name) { NULL }
#endif
#if OMPI_HAVE_FORTRAN_INTEGER
#define TWOLOC_FORTRAN_2INTEGER(name) { ompi_mpi_op_##name##_2integer }
#define TWOLOC_FORTRAN_2INTEGER_3BUFF(name) { ompi_mpi_op_three_buff_##name##_2integer }
#else
#define TWOLOC_FORTRAN_2INTEGER(name) { NULL }
#define TWOLOC_FORTRAN_2INTEGER_3BUFF(name) { NULL }
#endif

/** All "2" types *******************************************************/

#define TWOLOC(name) \
  TWOLOC_FORTRAN_2REAL(name),                 /* OMPI_OP_TYPE_2REAL */ \
  TWOLOC_FORTRAN_2DOUBLE_PRECISION(name),     /* OMPI_OP_TYPE_2DOUBLE_PRECISION */ \
  TWOLOC_FORTRAN_2INTEGER(name),              /* OMPI_OP_TYPE_2INTEGER */ \
  { ompi_mpi_op_##name##_float_int },         /* OMPI_OP_TYPE_FLOAT_INT */ \
  { ompi_mpi_op_##name##_double_int },        /* OMPI_OP_TYPE_DOUBLE_INT */ \
  { ompi_mpi_op_##name##_long_int },          /* OMPI_OP_TYPE_LONG_INT */ \
  { ompi_mpi_op_##name##_2int },              /* OMPI_OP_TYPE_2INT */ \
  { ompi_mpi_op_##name##_short_int },         /* OMPI_OP_TYPE_SHORT_INT */ \
  { ompi_mpi_op_##name##_long_double_int }    /* OMPI_OP_TYPE_LONG_DOUBLE_INT */

#define TWOLOC_3BUFF(name) \
  TWOLOC_FORTRAN_2REAL_3BUFF(name),                 /* OMPI_OP_TYPE_2REAL */ \
  TWOLOC_FORTRAN_2DOUBLE_PRECISION_3BUFF(name),     /* OMPI_OP_TYPE_2DOUBLE_PRECISION */ \
  TWOLOC_FORTRAN_2INTEGER_3BUFF(name),              /* OMPI_OP_TYPE_2INTEGER */ \
  { ompi_mpi_op_three_buff_##name##_float_int },         /* OMPI_OP_TYPE_FLOAT_INT */ \
  { ompi_mpi_op_three_buff_##name##_double_int },        /* OMPI_OP_TYPE_DOUBLE_INT */ \
  { ompi_mpi_op_three_buff_##name##_long_int },          /* OMPI_OP_TYPE_LONG_INT */ \
  { ompi_mpi_op_three_buff_##name##_2int },              /* OMPI_OP_TYPE_2INT */ \
  { ompi_mpi_op_three_buff_##name##_short_int },         /* OMPI_OP_TYPE_SHORT_INT */ \
  { ompi_mpi_op_three_buff_##name##_long_double_int }    /* OMPI_OP_TYPE_LONG_DOUBLE_INT */

#define TWOLOC_NULL \
  { NULL }, /* OMPI_OP_TYPE_2REAL */\
  { NULL }, /* OMPI_OP_TYPE_2DOUBLE_PRECISION */ \
  { NULL }, /* OMPI_OP_TYPE_2INTEGER */ \
  { NULL }, /* OMPI_OP_TYPE_FLOAT_INT */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE_INT */ \
  { NULL }, /* OMPI_OP_TYPE_LONG_INT */ \
  { NULL }, /* OMPI_OP_TYPE_2INT */ \
  { NULL }, /* OMPI_OP_TYPE_SHORT_INT */ \
  { NULL }  /* OMPI_OP_TYPE_LONG_DOUBLE_INT */

#define TWOLOC_NULL_3BUFF \
  { NULL }, /* OMPI_OP_TYPE_2REAL */\
  { NULL }, /* OMPI_OP_TYPE_2DOUBLE_PRECISION */ \
  { NULL }, /* OMPI_OP_TYPE_2INTEGER */ \
  { NULL }, /* OMPI_OP_TYPE_FLOAT_INT */ \
  { NULL }, /* OMPI_OP_TYPE_DOUBLE_INT */ \
  { NULL }, /* OMPI_OP_TYPE_LONG_INT */ \
  { NULL }, /* OMPI_OP_TYPE_2INT */ \
  { NULL }, /* OMPI_OP_TYPE_SHORT_INT */ \
  { NULL }  /* OMPI_OP_TYPE_LONG_DOUBLE_INT */


/*
 * MPI_OP_NULL
 * All types
 */
#define FLAGS_NO_FLOAT \
    (OMPI_OP_FLAGS_INTRINSIC | OMPI_OP_FLAGS_ASSOC | OMPI_OP_FLAGS_COMMUTE)
#define FLAGS \
    (OMPI_OP_FLAGS_INTRINSIC | OMPI_OP_FLAGS_ASSOC | \
     OMPI_OP_FLAGS_FLOAT_ASSOC | OMPI_OP_FLAGS_COMMUTE)

ompi_predefined_op_t ompi_mpi_op_null = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_OP_NULL",
    FLAGS,
    { C_INTEGER_NULL,
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_NULL_3BUFF,
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_MAX
 * C integer, Fortran integer, Floating point
 */
ompi_predefined_op_t ompi_mpi_op_max = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_MAX",
    FLAGS,
    { C_INTEGER(max),
      FORTRAN_INTEGER(max),
      FLOATING_POINT(max),
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(max),
      FORTRAN_INTEGER_3BUFF(max),
      FLOATING_POINT_3BUFF(max),
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_MIN
 */
ompi_predefined_op_t ompi_mpi_op_min = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_MIN",
    FLAGS,
    { C_INTEGER(min),
      FORTRAN_INTEGER(min),
      FLOATING_POINT(min),
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(min),
      FORTRAN_INTEGER_3BUFF(min),
      FLOATING_POINT_3BUFF(min),
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_SUM
 */
ompi_predefined_op_t ompi_mpi_op_sum = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_SUM",
    FLAGS_NO_FLOAT,
    { C_INTEGER(sum),
      FORTRAN_INTEGER(sum),
      FLOATING_POINT(sum),
      LOGICAL_NULL,
      COMPLEX(sum),
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(sum),
      FORTRAN_INTEGER_3BUFF(sum),
      FLOATING_POINT_3BUFF(sum),
      LOGICAL_NULL_3BUFF,
      COMPLEX_3BUFF(sum),
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_PROD
 */
ompi_predefined_op_t ompi_mpi_op_prod = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_PROD",
    FLAGS_NO_FLOAT,
    { C_INTEGER(prod),
      FORTRAN_INTEGER(prod),
      FLOATING_POINT(prod),
      LOGICAL_NULL,
      COMPLEX(prod),
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(prod),
      FORTRAN_INTEGER_3BUFF(prod),
      FLOATING_POINT_3BUFF(prod),
      LOGICAL_NULL_3BUFF,
      COMPLEX_3BUFF(prod),
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_LAND
 */
ompi_predefined_op_t ompi_mpi_op_land = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_LAND",
    FLAGS,
    { C_INTEGER(land),
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL(land),
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(land),
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_3BUFF(land),
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_BAND
 */
ompi_predefined_op_t ompi_mpi_op_band = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_BAND",
    FLAGS,
    { C_INTEGER(band),
      FORTRAN_INTEGER(band),
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE(band),
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(band),
      FORTRAN_INTEGER_3BUFF(band),
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_3BUFF(band),
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_LOR
 */
ompi_predefined_op_t ompi_mpi_op_lor = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_LOR",
    FLAGS,
    { C_INTEGER(lor),
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL(lor),
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(lor),
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_3BUFF(lor),
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_BOR
 */
ompi_predefined_op_t ompi_mpi_op_bor = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_BOR",
    FLAGS,
    { C_INTEGER(bor),
      FORTRAN_INTEGER(bor),
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE(bor),
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(bor),
      FORTRAN_INTEGER_3BUFF(bor),
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_3BUFF(bor),
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_LXOR
 */
ompi_predefined_op_t ompi_mpi_op_lxor = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_LXOR",
    FLAGS,
    { C_INTEGER(lxor),
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL(lxor),
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(lxor),
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_3BUFF(lxor),
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_BXOR
 */
ompi_predefined_op_t ompi_mpi_op_bxor = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_BXOR",
    FLAGS,
    { C_INTEGER(bxor),
      FORTRAN_INTEGER(bxor),
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE(bxor),
      TWOLOC_NULL },
    -1,
    { C_INTEGER_3BUFF(bxor),
      FORTRAN_INTEGER_3BUFF(bxor),
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_3BUFF(bxor),
      TWOLOC_NULL_3BUFF }
    }};


/*
 * MPI_OP_MAXLOC
 */
ompi_predefined_op_t ompi_mpi_op_maxloc = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_MAXLOC",
    (OMPI_OP_FLAGS_INTRINSIC | OMPI_OP_FLAGS_ASSOC | OMPI_OP_FLAGS_COMMUTE),
    { C_INTEGER_NULL,
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC(maxloc) },
    -1,
    { C_INTEGER_NULL_3BUFF,
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_3BUFF(maxloc) }
    }};


/*
 * MPI_OP_MINLOC
 */
ompi_predefined_op_t ompi_mpi_op_minloc = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_MINLOC",
    FLAGS,
    { C_INTEGER_NULL,
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC(minloc) },
    -1,
    { C_INTEGER_NULL_3BUFF,
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_3BUFF(minloc) }
    }};

/*
 * MPI_OP_REPLACE
 * (MPI_ACCUMULATE is handled differently than the other reductions,
 * so just zero out its function impementations here to ensure that
 * users don't invoke MPI_REPLACE with any reduction operations other
 * than ACCUMULATE)
 */
ompi_predefined_op_t ompi_mpi_op_replace = {{
    OPAL_OBJ_STATIC_INIT(opal_object_t),

    "MPI_REPLACE",
    FLAGS,
    { C_INTEGER_NULL,
      FORTRAN_INTEGER_NULL,
      FLOATING_POINT_NULL,
      LOGICAL_NULL,
      COMPLEX_NULL,
      BYTE_NULL,
      TWOLOC_NULL },
    -1,
    { C_INTEGER_NULL_3BUFF,
      FORTRAN_INTEGER_NULL_3BUFF,
      FLOATING_POINT_NULL_3BUFF,
      LOGICAL_NULL_3BUFF,
      COMPLEX_NULL_3BUFF,
      BYTE_NULL_3BUFF,
      TWOLOC_NULL_3BUFF }
    }};

/*
 * Map from ddt->id to position in op function pointer array
 */
int ompi_op_ddt_map[DT_MAX_PREDEFINED];


/*
 * Initialize OMPI op infrastructure
 */
int ompi_op_init(void)
{
    int i;

  /* initialize ompi_op_f_to_c_table */

    ompi_op_f_to_c_table = OBJ_NEW(opal_pointer_array_t);
    if (NULL == ompi_op_f_to_c_table){
        return OMPI_ERROR;
    }

    /* Fill in the ddt.id->op_position map */

    for (i = 0; i < DT_MAX_PREDEFINED; ++i) {
        ompi_op_ddt_map[i] = -1;
    }

    ompi_op_ddt_map[DT_UNSIGNED_CHAR] = OMPI_OP_TYPE_UNSIGNED_CHAR;
    ompi_op_ddt_map[DT_SIGNED_CHAR] = OMPI_OP_TYPE_SIGNED_CHAR;
    ompi_op_ddt_map[DT_BYTE] = OMPI_OP_TYPE_BYTE;
    ompi_op_ddt_map[DT_SHORT] = OMPI_OP_TYPE_SHORT;
    ompi_op_ddt_map[DT_UNSIGNED_SHORT] = OMPI_OP_TYPE_UNSIGNED_SHORT;
    ompi_op_ddt_map[DT_INT] = OMPI_OP_TYPE_INT;
    ompi_op_ddt_map[DT_UNSIGNED_INT] = OMPI_OP_TYPE_UNSIGNED;
    ompi_op_ddt_map[DT_LONG] = OMPI_OP_TYPE_LONG;
    ompi_op_ddt_map[DT_UNSIGNED_LONG] = OMPI_OP_TYPE_UNSIGNED_LONG;
    ompi_op_ddt_map[DT_LONG_LONG_INT] = OMPI_OP_TYPE_LONG_LONG_INT;
    ompi_op_ddt_map[DT_UNSIGNED_LONG_LONG] = OMPI_OP_TYPE_UNSIGNED_LONG_LONG;
    ompi_op_ddt_map[DT_FLOAT] = OMPI_OP_TYPE_FLOAT;
    ompi_op_ddt_map[DT_DOUBLE] = OMPI_OP_TYPE_DOUBLE;
    ompi_op_ddt_map[DT_LONG_DOUBLE] = OMPI_OP_TYPE_LONG_DOUBLE;
    ompi_op_ddt_map[DT_COMPLEX_FLOAT] = OMPI_OP_TYPE_COMPLEX;
    ompi_op_ddt_map[DT_COMPLEX_DOUBLE] = OMPI_OP_TYPE_DOUBLE_COMPLEX;
    ompi_op_ddt_map[DT_LOGIC] = OMPI_OP_TYPE_LOGICAL;
    ompi_op_ddt_map[DT_CXX_BOOL] = OMPI_OP_TYPE_BOOL;
    ompi_op_ddt_map[DT_FLOAT_INT] = OMPI_OP_TYPE_FLOAT_INT;
    ompi_op_ddt_map[DT_DOUBLE_INT] = OMPI_OP_TYPE_DOUBLE_INT;
    ompi_op_ddt_map[DT_LONG_INT] = OMPI_OP_TYPE_LONG_INT;
    ompi_op_ddt_map[DT_2INT] = OMPI_OP_TYPE_2INT;
    ompi_op_ddt_map[DT_SHORT_INT] = OMPI_OP_TYPE_SHORT_INT;
    ompi_op_ddt_map[DT_INTEGER] = OMPI_OP_TYPE_INTEGER;
    ompi_op_ddt_map[DT_REAL] = OMPI_OP_TYPE_REAL;
    ompi_op_ddt_map[DT_DBLPREC] = OMPI_OP_TYPE_DOUBLE_PRECISION;
    ompi_op_ddt_map[DT_2REAL] = OMPI_OP_TYPE_2REAL;
    ompi_op_ddt_map[DT_2DBLPREC] = OMPI_OP_TYPE_2DOUBLE_PRECISION;
    ompi_op_ddt_map[DT_2INTEGER] = OMPI_OP_TYPE_2INTEGER;
    ompi_op_ddt_map[DT_LONG_DOUBLE_INT] = OMPI_OP_TYPE_LONG_DOUBLE_INT;
    ompi_op_ddt_map[DT_WCHAR] = OMPI_OP_TYPE_WCHAR;

    /* Create the intrinsic ops */

    if (add_intrinsic(&ompi_mpi_op_null.op, OMPI_OP_FORTRAN_NULL) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_max.op, OMPI_OP_FORTRAN_MAX) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_min.op, OMPI_OP_FORTRAN_MIN) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_sum.op, OMPI_OP_FORTRAN_SUM) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_prod.op, OMPI_OP_FORTRAN_PROD) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_land.op, OMPI_OP_FORTRAN_LAND) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_band.op, OMPI_OP_FORTRAN_BAND) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_lor.op, OMPI_OP_FORTRAN_LOR) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_bor.op, OMPI_OP_FORTRAN_BOR) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_lxor.op, OMPI_OP_FORTRAN_LXOR) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_bxor.op, OMPI_OP_FORTRAN_BXOR) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_maxloc.op, 
                      OMPI_OP_FORTRAN_MAXLOC) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_minloc.op, 
                    OMPI_OP_FORTRAN_MINLOC) != OMPI_SUCCESS ||
        add_intrinsic(&ompi_mpi_op_replace.op, 
                      OMPI_OP_FORTRAN_REPLACE) != OMPI_SUCCESS) {
        return OMPI_ERROR;
    }

    /* All done */
    return OMPI_SUCCESS;
}


/*
 * Clean up the op resources
 */
int ompi_op_finalize(void)
{
    /* clean up the intrinsic ops */

    OBJ_DESTRUCT(&ompi_mpi_op_minloc);
    OBJ_DESTRUCT(&ompi_mpi_op_maxloc);
    OBJ_DESTRUCT(&ompi_mpi_op_bxor);
    OBJ_DESTRUCT(&ompi_mpi_op_lxor);
    OBJ_DESTRUCT(&ompi_mpi_op_bor);
    OBJ_DESTRUCT(&ompi_mpi_op_lor);
    OBJ_DESTRUCT(&ompi_mpi_op_band);
    OBJ_DESTRUCT(&ompi_mpi_op_land);
    OBJ_DESTRUCT(&ompi_mpi_op_prod);
    OBJ_DESTRUCT(&ompi_mpi_op_sum);
    OBJ_DESTRUCT(&ompi_mpi_op_min);
    OBJ_DESTRUCT(&ompi_mpi_op_max);
    OBJ_DESTRUCT(&ompi_mpi_op_null);

    /* Remove op F2C table */

    OBJ_RELEASE(ompi_op_f_to_c_table);

    /* All done */

    return OMPI_SUCCESS;
}


/*
 * Create a new MPI_Op
 */
ompi_op_t *ompi_op_create(bool commute,
                          ompi_op_fortran_handler_fn_t *func)
{
    int i;
    ompi_op_t *new_op;

    /* Create a new object and ensure that it's valid */
    new_op = OBJ_NEW(ompi_op_t);

    if (NULL == new_op)
        goto error;

    if (OMPI_ERROR == new_op->o_f_to_c_index) {
        OBJ_RELEASE(new_op);
        new_op = NULL;
        goto error;
    }

    /*
     * The new object is valid -- initialize it.  If this is being
     *  created from fortran, the fortran MPI API wrapper function
     *  will override the o_flags field directly.  We cast the
     *  function pointer type to the fortran type arbitrarily -- it
     *  only has to be a function pointer in order to store properly,
     *  it doesn't matter what type it is (we'll cast it to the Right
     *  type when we *use* it).
     */
    new_op->o_flags = OMPI_OP_FLAGS_ASSOC;
    if (commute) {
        new_op->o_flags |= OMPI_OP_FLAGS_COMMUTE;
    }

    new_op->o_func[0].fort_fn = func;
    for (i = 1; i < OMPI_OP_TYPE_MAX; ++i) {
        new_op->o_func[i].fort_fn = NULL;
    }

error:
    /* All done */
    return new_op;
}


void ompi_op_set_cxx_callback(ompi_op_t *op, MPI_User_function *fn)
{
    op->o_flags |= OMPI_OP_FLAGS_CXX_FUNC;
    op->o_func[1].c_fn = fn;
}


/**************************************************************************
 *
 * Static functions
 *
 **************************************************************************/

static int add_intrinsic(ompi_op_t *op, int fort_handle)
{
    /* Add the op to the table */

    OBJ_CONSTRUCT(op, ompi_op_t);
    if (op->o_f_to_c_index != fort_handle) {
        return OMPI_ERROR;
    }

    /* All done */
    return OMPI_SUCCESS;
}  


/*
 * Op constructor
 */
static void ompi_op_construct(ompi_op_t *new_op)
{
    int ret_val;

    /* assign entry in fortran <-> c translation array */

    ret_val = opal_pointer_array_add(ompi_op_f_to_c_table, new_op);
    new_op->o_f_to_c_index = ret_val;
}


/*
 * Op destructor
 */
static void ompi_op_destruct(ompi_op_t *op)
{
    /* reset the ompi_op_f_to_c_table entry - make sure that the
       entry is in the table */

    if (NULL != opal_pointer_array_get_item(ompi_op_f_to_c_table,
                                            op->o_f_to_c_index)) {
        opal_pointer_array_set_item(ompi_op_f_to_c_table,
                                    op->o_f_to_c_index, NULL);
    }
}
