/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

/*

  Exercise communicator routines for intercommunicators

  This C version derived from attrt, which in turn was
  derived from a Fortran test program from ...

 */
#include "mpitest.h"

#ifdef MULTI_TESTS
#define run attr_attric
int run(const char *arg);
#endif

/* #define DEBUG */
static int test_communicators(void);
static int copy_fn(MPI_Comm, int, void *, void *, void *, int *);
static int delete_fn(MPI_Comm, int, void *, void *);
#ifdef DEBUG
#define FFLUSH fflush(stdout);
#else
#define FFLUSH
#endif

int run(const char *arg)
{
    return test_communicators();
}

static int copy_fn(MPI_Comm oldcomm, int keyval, void *extra_state,
                   void *attribute_val_in, void *attribute_val_out, int *flag)
{
    /* Note that if (sizeof(int) < sizeof(void *), just setting the int
     * part of attribute_val_out may leave some dirty bits
     */
    *(MPI_Aint *) attribute_val_out = (MPI_Aint) attribute_val_in;
    *flag = 1;
    return MPI_SUCCESS;
}

static int delete_fn(MPI_Comm comm, int keyval, void *attribute_val, void *extra_state)
{
    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    if ((MPI_Aint) attribute_val != (MPI_Aint) world_rank) {
        printf("incorrect attribute value %d\n", *(int *) attribute_val);
        MPI_Abort(MPI_COMM_WORLD, 1005);
    }
    return MPI_SUCCESS;
}

static int test_communicators(void)
{
    MPI_Comm dup_comm, comm;
    void *vvalue;
    int flag, world_rank, world_size, key_1, key_3;
    int errs = 0;
    MPI_Aint value;
    int isLeft;

    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
#ifdef DEBUG
    if (world_rank == 0) {
        printf("*** Communicators ***\n");
        fflush(stdout);
    }
#endif

    while (MTestGetIntercomm(&comm, &isLeft, 2)) {
        MTestPrintfMsg(1, "start while loop, isLeft=%s\n", (isLeft ? "TRUE" : "FALSE"));

        if (comm == MPI_COMM_NULL) {
            MTestPrintfMsg(1, "got COMM_NULL, skipping\n");
            continue;
        }

        /*
         * Check Comm_dup by adding attributes to comm & duplicating
         */

        value = 9;
        MPI_Keyval_create(copy_fn, delete_fn, &key_1, &value);
        MTestPrintfMsg(1, "Keyval_create key=%#x value=%d\n", key_1, value);
        value = 7;
        MPI_Keyval_create(MPI_NULL_COPY_FN, MPI_NULL_DELETE_FN, &key_3, &value);
        MTestPrintfMsg(1, "Keyval_create key=%#x value=%d\n", key_3, value);

        /* This may generate a compilation warning; it is, however, an
         * easy way to cache a value instead of a pointer */
        /* printf("key1 = %x key3 = %x\n", key_1, key_3); */
        MPI_Attr_put(comm, key_1, (void *) (MPI_Aint) world_rank);
        MPI_Attr_put(comm, key_3, (void *) 0);

        MTestPrintfMsg(1, "Comm_dup\n");
        MPI_Comm_dup(comm, &dup_comm);

        /* Note that if sizeof(int) < sizeof(void *), we can't use
         * (void **)&value to get the value we passed into Attr_put.  To avoid
         * problems (e.g., alignment errors), we recover the value into
         * a (void *) and cast to int. Note that this may generate warning
         * messages from the compiler.  */
        MPI_Attr_get(dup_comm, key_1, (void **) &vvalue, &flag);
        value = (MPI_Aint) vvalue;

        if (!flag) {
            errs++;
            printf("dup_comm key_1 not found on %d\n", world_rank);
            fflush(stdout);
            MPI_Abort(MPI_COMM_WORLD, 3004);
        }

        if (value != world_rank) {
            errs++;
            printf("dup_comm key_1 value incorrect: %ld\n", (long) value);
            fflush(stdout);
            MPI_Abort(MPI_COMM_WORLD, 3005);
        }

        MPI_Attr_get(dup_comm, key_3, (void **) &vvalue, &flag);
        value = (MPI_Aint) vvalue;
        if (flag) {
            errs++;
            printf("dup_comm key_3 found!\n");
            fflush(stdout);
            MPI_Abort(MPI_COMM_WORLD, 3008);
        }
        MTestPrintfMsg(1, "Keyval_free key=%#x\n", key_1);
        MPI_Keyval_free(&key_1);
        MTestPrintfMsg(1, "Keyval_free key=%#x\n", key_3);
        MPI_Keyval_free(&key_3);
        /*
         * Free all communicators created
         */
        MTestPrintfMsg(1, "Comm_free comm\n");
        MPI_Comm_free(&comm);
        MTestPrintfMsg(1, "Comm_free dup_comm\n");
        MPI_Comm_free(&dup_comm);
    }

    return errs;
}
