/**
 * @file communicator.c
 *
 * @copyright Copyright  (C)  2013 Moritz Hanke <hanke@dkrz.de>
 *                                 Rene Redler <rene.redler@mpimet.mpg.de>
 *
 * @version 1.0
 * @author Moritz Hanke <hanke@dkrz.de>
 *         Rene Redler <rene.redler@mpimet.mpg.de>
 */
/*
 * Keywords:
 * Maintainer: Moritz Hanke <hanke@dkrz.de>
 *             Rene Redler <rene.redler@mpimet.mpg.de>
 * URL: https://doc.redmine.dkrz.de/YAC/html/index.html
 *
 * This file is part of YAC.
 *
 * YAC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * YAC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with YAC.  If not, see <http://www.gnu.org/licenses/gpl.txt>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "communicator.h"
//#include "communicator_local.h"
//#include "communicator_mpi.h"
#include "utils.h"

// #define YAC_DEBUG_COMMUNICATION
// #define YAC_DEBUG_COMMUNICATION_TAGS

static const struct comm_request comm_request_null_ = {.waitall = NULL,
                                                 .testsome = NULL,
                                                 .free_request = NULL};
const struct comm_request * const COMM_REQUEST_NULL = &comm_request_null_;

static void (**finalize_callback)(void) = NULL;
static int num_finalize_callback = 0;

unsigned yac_get_comm_size(struct communicator * comm) {

   if (comm->vtable->get_size == NULL) {
      yac_internal_abort_message("ERROR: routine get_comm_size not implemented",
                                 __FILE__, __LINE__);
      return -1;
   }

   return comm->vtable->get_size(comm);
}

unsigned yac_get_comm_rank(struct communicator * comm) {

   if (comm->vtable->get_size == NULL) {
      yac_internal_abort_message("ERROR: routine get_comm_rank not implemented",
                                 __FILE__, __LINE__);
      return -1;
   }

   return comm->vtable->get_rank(comm);
}

void yac_comm_bsend(void const * send_buffer, unsigned count,
                enum yac_comm_data_type data_type, unsigned dest, unsigned tag,
                struct communicator * comm) {

   if (comm->vtable->bsend == NULL)
      yac_internal_abort_message("ERROR: routine comm_bsend not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_bsend: count %d src %d dest %d, tag %d\n",
           count, yac_get_comm_rank(comm), dest, tag);
#endif

   comm->vtable->bsend(send_buffer, count, data_type, dest, tag, comm);
}

void yac_comm_isend(void const * send_buffer, unsigned count,
                    enum yac_comm_data_type data_type, unsigned dest,
                    unsigned tag, struct communicator * comm,
                    struct comm_request ** request) {

   if (comm->vtable->isend == NULL)
      yac_internal_abort_message(
         "ERROR: routine yac_comm_isend not implemented", __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator yac_comm_isend: count %d src %d dest %d, "
           "tag %d\n", count, yac_get_comm_rank(comm), dest, tag);
#endif

   comm->vtable->isend(send_buffer, count, data_type, dest, tag, comm, request);
}

void yac_comm_recv_callback(unsigned count, enum yac_comm_data_type data_type,
                            unsigned source, unsigned tag,
                            struct communicator * comm, void * user_data,
                            func_recv_callback recv_callback,
                            func_recv_callback_cancel recv_callback_cancel,
                            enum yac_message_priority priority) {

   if (comm->vtable->recv_callback == NULL)
      yac_internal_abort_message("ERROR: routine comm_recv_callback not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_recv_callback: count %d src %d dest %d, "
           "tag %d\n", count, source, yac_get_comm_rank(comm), tag);
#endif

   if ((int)priority < 0)
      yac_internal_abort_message("ERROR(comm_recv_callback): invalid priority",
                                 __FILE__, __LINE__);

   comm->vtable->recv_callback(count, data_type, source, tag, comm, user_data,
                               recv_callback, recv_callback_cancel, priority);
}

void yac_comm_irecv(void * recv_buffer, unsigned count,
                    enum yac_comm_data_type data_type, unsigned source,
                    unsigned tag, struct communicator * comm,
                    struct comm_request ** request) {

   if (comm->vtable->irecv == NULL)
      yac_internal_abort_message(
         "ERROR: routine yac_comm_irecv not implemented", __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator yac_comm_irecv: count %d src %d dest %d, "
           "tag %d\n", count, source, yac_get_comm_rank(comm), tag);
#endif

   comm->vtable->irecv(recv_buffer, count, data_type, source, tag, comm,
                       request);
}

void yac_comm_recv(void * recv_buffer, unsigned * count,
                   enum yac_comm_data_type data_type, unsigned source,
                   unsigned tag, struct communicator * comm) {

   if (comm->vtable->recv == NULL)
      yac_internal_abort_message("ERROR: routine comm_recv not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_recv: count %d src %d dest %d, tag %d\n",
           *count, source, yac_get_comm_rank(comm), tag);
#endif

   comm->vtable->recv(recv_buffer, count, data_type, source, tag, comm);
}

unsigned yac_comm_wait(struct communicator * comm, unsigned tag) {

   if (comm->vtable->wait == NULL)
      yac_internal_abort_message("ERROR: routine comm_wait not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_wait: rank %d, tag %d\n",
           yac_get_comm_rank(comm), tag);
   fflush(stdout);
   fflush(stderr);
#endif

   return comm->vtable->wait(comm, tag);
}

void yac_comm_waitall(unsigned count, struct comm_request ** requests) {

   for (unsigned i = 0; i < count; ++i) {
      if (requests[i] != COMM_REQUEST_NULL) {
         if (requests[i]->waitall == NULL)
            yac_internal_abort_message(
               "ERROR: routine yac_comm_waitall not implemented",
               __FILE__, __LINE__);
         requests[i]->waitall(count, requests);
         return;
      }
   }
}

unsigned yac_comm_testsome(unsigned count, struct comm_request ** requests) {

   for (unsigned i = 0; i < count; ++i) {
      if (requests[i] != COMM_REQUEST_NULL) {
          if (requests[i]->testsome == NULL)
            yac_internal_abort_message(
            "ERROR: routine yac_comm_testsome not implemented",
            __FILE__, __LINE__);
         return requests[i]->testsome(count, requests);
      }
   }

   return count;
}

void yac_comm_free_request(struct comm_request ** request) {

  if (*request == COMM_REQUEST_NULL) return;

  if ((*request)->free_request == NULL)
    yac_internal_abort_message("ERROR: routine free_request not implemented",
                               __FILE__, __LINE__);

   (*request)->free_request(request);
   *request = (struct comm_request *)COMM_REQUEST_NULL;
}

void yac_comm_cancel_recv(struct communicator * comm, unsigned tag) {

   if (comm->vtable->cancel_recv == NULL)
      yac_internal_abort_message("ERROR: routine comm_cancel_recv not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_cancel_recv: src %d, tag %d\n",
           yac_get_comm_rank(comm), tag);
#endif

   comm->vtable->cancel_recv(comm, tag);
}

void yac_get_unique_comm_tags(struct communicator * comm,
                              char const * unique_generator_string,
                              char const ** tag_strings,
                              unsigned num_tag_strings, unsigned * tags) {

  if (comm->vtable->get_unique_tags == NULL)
    yac_internal_abort_message(
      "ERROR: routine get_unique_comm_tags not implemented",
      __FILE__, __LINE__);

  comm->vtable->get_unique_tags(
    comm, unique_generator_string, tag_strings, num_tag_strings, tags);

#if !(defined(YAC_DEBUG_COMMUNICATION) || defined(YAC_DEBUG_COMMUNICATION_TAGS))
  if (0)
#endif
    for (unsigned i = 0; i < num_tag_strings; ++i)
      fprintf(stderr, "communicator yac_get_unique_comm_tags: "
              "rank %u string %s tag %u\n", yac_get_comm_rank(comm),
              tag_strings[i], tags[i]);
}

void yac_free_communicator(struct communicator * comm) {

   if (comm->vtable->free == NULL)
      yac_internal_abort_message("ERROR: routine free_communicator not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator free_communicator: rank %d\n",
           yac_get_comm_rank(comm));
#endif

   comm->vtable->free(comm);
}

void yac_comm_allgather(void * send_buffer, unsigned send_count,
                        void * recv_buffer, unsigned recv_count,
                        enum yac_comm_data_type data_type, struct communicator * comm) {

   if (comm->vtable->allgather == NULL)
      yac_internal_abort_message("ERROR: routine comm_allgather not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_allgather: src %d\n",
           yac_get_comm_rank(comm));
#endif

   comm->vtable->allgather(send_buffer, send_count, recv_buffer, recv_count,
                           data_type, comm);
}

void yac_comm_allgatherv(void * send_buffer, unsigned send_count,
                         void * recv_buffer, unsigned * recv_counts,
                         enum yac_comm_data_type data_type, struct communicator * comm){

   if (comm->vtable->allgatherv == NULL)
      yac_internal_abort_message("ERROR: routine comm_allgatherv not implemented",
                    __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_allgatherv: src %d\n",
           yac_get_comm_rank(comm));
#endif

   comm->vtable->allgatherv(send_buffer, send_count, recv_buffer, recv_counts,
                             data_type, comm);
}

struct communicator * yac_comm_dup(struct communicator * comm) {

   if (comm->vtable->dup == NULL)
      yac_internal_abort_message("ERROR: routine comm_dup not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_dup: src %d \n",
           yac_get_comm_rank(comm));
#endif

   return comm->vtable->dup(comm);
}

struct communicator * yac_comm_split(struct communicator * comm,
                                     int color, int key) {

   if (comm->vtable->split == NULL)
      yac_internal_abort_message("ERROR: routine comm_split not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_split: rank %d, color %d, key %d\n",
           yac_get_comm_rank(comm), color, key);
#endif

   return comm->vtable->split(comm, color, key);
}

struct communicator * yac_comm_split_intercomm(struct communicator * comm,
                                               int group,
                                               unsigned * local_group_ranks,
                                               unsigned * remote_group_ranks) {

   if (comm->vtable->split_intercomm == NULL)
      yac_internal_abort_message("ERROR: routine comm_split_intercomm not implemented",
                                 __FILE__, __LINE__);

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_split_intercomm: rank %d, group %d\n",
           yac_get_comm_rank(comm), group);
#endif

   return comm->vtable->split_intercomm(comm, group, local_group_ranks,
                                        remote_group_ranks);
}

unsigned yac_get_comm_remote_size(struct communicator * comm) {

   if (comm->vtable->get_remote_size == NULL)
      yac_internal_abort_message("ERROR: routine get_comm_remote_size not implemented",
                                  __FILE__, __LINE__);

   return comm->vtable->get_remote_size(comm);
}

void yac_comm_abort(struct communicator * comm) {

   if (comm->vtable->abort == NULL)
      yac_internal_abort_message("ERROR: routine comm_abort not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->abort(comm);
}

int yac_comm_pack_size(int count, enum yac_comm_data_type type,
                       struct communicator * comm) {

   if (comm->vtable->pack_size == NULL)
      yac_internal_abort_message("ERROR: routine comm_pack_size not implemented",
                                  __FILE__, __LINE__);

   return comm->vtable->pack_size(count, type, comm);
}

void yac_comm_pack(void * in_buffer, int in_count, enum yac_comm_data_type type,
                   void * out_buffer, int out_buffer_size, int * position,
                   struct communicator * comm) {

   if (comm->vtable->pack == NULL)
      yac_internal_abort_message("ERROR: routine comm_pack not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->pack(in_buffer, in_count, type, out_buffer, out_buffer_size,
                      position, comm);
}

void yac_comm_unpack(void * in_buffer, int in_count, int * position,
                     void * out_buffer, int out_buffer_size, enum yac_comm_data_type type,
                     struct communicator * comm) {

   if (comm->vtable->unpack == NULL)
      yac_internal_abort_message("ERROR: routine comm_unpack not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->unpack(in_buffer, in_count, position, out_buffer,
                        out_buffer_size, type, comm);
}

void yac_comm_translate_ranks(struct communicator * comm_a,
                              struct communicator * comm_b, unsigned * ranks) {

   if (comm_a->vtable->translate_ranks == NULL)
      yac_internal_abort_message("ERROR: routine comm_translate_ranks not implemented",
                                 __FILE__, __LINE__);

   if (comm_a->vtable != comm_b->vtable)
      yac_internal_abort_message ("ERROR: communicators are of different type.",
                                  __FILE__, __LINE__);

   comm_a->vtable->translate_ranks(comm_a, comm_b, ranks);
}

void yac_comm_bcast_callback(void * buffer, unsigned count,
                             enum yac_comm_data_type data_type, unsigned root,
                             unsigned tag, struct communicator * comm,
                             void * user_data, func_recv_callback bcast_callback,
                             enum yac_message_priority priority) {

   if (comm->vtable->bcast_callback == NULL)
      yac_internal_abort_message("ERROR: comm_bcast_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->bcast_callback(buffer, count, data_type, root, tag, comm,
                                user_data, bcast_callback, priority);
}

void yac_comm_gatherv_callback(void * send_buffer, unsigned * counts,
                               enum yac_comm_data_type data_type, unsigned root,
                               unsigned tag, struct communicator * comm,
                               void * user_data,
                               func_recv_callback gatherv_callback,
                               enum yac_message_priority priority) {

   if (comm->vtable->gatherv_callback == NULL)
      yac_internal_abort_message("ERROR: comm_gatherv_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->gatherv_callback(send_buffer, counts, data_type, root, tag,
                                  comm, user_data, gatherv_callback, priority);
}

void yac_comm_allgather_callback(void * send_buffer, unsigned count,
                                 enum yac_comm_data_type data_type,
                                 unsigned tag, struct communicator * comm,
                                 void * user_data,
                                 func_recv_callback allgather_callback,
                                 enum yac_message_priority priority) {

   if (comm->vtable->allgather_callback == NULL)
      yac_internal_abort_message("ERROR: comm_allgather_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->allgather_callback(send_buffer, count, data_type, tag, comm,
                                    user_data, allgather_callback, priority);
}

void yac_comm_allgatherv_callback(void * send_buffer, unsigned * counts,
                                  enum yac_comm_data_type data_type,
                                  unsigned tag, struct communicator * comm,
                                  void * user_data,
                                  func_recv_callback allgatherv_callback,
                                  enum yac_message_priority priority) {

   if (comm->vtable->allgatherv_callback == NULL)
      yac_internal_abort_message("ERROR: comm_allgatherv_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->allgatherv_callback(send_buffer, counts, data_type, tag, comm,
                                     user_data, allgatherv_callback, priority);
}

void yac_comm_scatter_callback(void * send_buffer, unsigned count,
                               enum yac_comm_data_type data_type, unsigned root,
                               unsigned tag, struct communicator * comm,
                               void * user_data,
                               func_recv_callback scatter_callback,
                               enum yac_message_priority priority) {

   if (comm->vtable->scatter_callback == NULL)
      yac_internal_abort_message("ERROR: comm_scatter_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->scatter_callback(
      send_buffer, count, data_type, root, tag, comm, user_data,
      scatter_callback, priority);
}

void yac_comm_scatterv_callback(void * send_buffer, unsigned * counts,
                                enum yac_comm_data_type data_type, unsigned root,
                                unsigned tag, struct communicator * comm,
                                void * user_data,
                                func_recv_callback scatterv_callback,
                                enum yac_message_priority priority) {

   if (comm->vtable->scatterv_callback == NULL)
      yac_internal_abort_message("ERROR: comm_scatterv_callback not implemented",
                                 __FILE__, __LINE__);

   comm->vtable->scatterv_callback(send_buffer, counts, data_type, root, tag,
                                  comm, user_data, scatterv_callback, priority);
}

void yac_comm_alltoallv_callback(void * send_buffer, unsigned * counts,
                                 enum yac_comm_data_type data_type,
                                 unsigned tag, struct communicator * comm,
                                 void * user_data,
                                 func_recv_callback alltoallv_callback,
                                 enum yac_message_priority priority) {

   if (comm->vtable->alltoallv_callback == NULL)
      yac_internal_abort_message(
        "ERROR: comm_alltoallv_callback not implemented", __FILE__, __LINE__);

   comm->vtable->alltoallv_callback(send_buffer, counts, data_type, tag, comm,
                                    user_data, alltoallv_callback, priority);
}

void yac_comm_reduce_callback(void * buffer, unsigned count,
                              enum yac_comm_data_type data_type,
                              func_comm_op op, unsigned root, unsigned tag,
                              struct communicator * comm, void * user_data,
                              func_recv_callback reduce_callback,
                              enum yac_message_priority priority) {

   if (comm->vtable->reduce_callback == NULL)
      yac_internal_abort_message(
        "ERROR: comm_reduce_callback not implemented", __FILE__, __LINE__);

   comm->vtable->reduce_callback(buffer, count, data_type, op, root, tag, comm,
                                 user_data, reduce_callback, priority);
}

void yac_comm_allreduce_callback(void * buffer, unsigned count,
                                 enum yac_comm_data_type data_type,
                                 func_comm_op op, unsigned tag,
                                 struct communicator * comm, void * user_data,
                                 func_recv_callback allreduce_callback,
                                 enum yac_message_priority priority) {

   if (comm->vtable->allreduce_callback == NULL)
      yac_internal_abort_message(
        "ERROR: comm_allreduce_callback not implemented", __FILE__, __LINE__);

   comm->vtable->allreduce_callback(buffer, count, data_type, op, tag, comm,
                                    user_data, allreduce_callback, priority);
}

void yac_comm_barrier_callback(unsigned tag, struct communicator * comm,
                               void * user_data,
                               func_recv_callback barrier_callback,
                               enum yac_message_priority priority) {

   if (comm->vtable->barrier_callback == NULL)
      yac_internal_abort_message(
        "ERROR: comm_barrier_callback not implemented", __FILE__, __LINE__);

   comm->vtable->barrier_callback(tag, comm, user_data, barrier_callback,
                                  priority);
}

void yac_add_comm_finalize_callback(void (*callback)(void)) {

   for (int i = 0; i < num_finalize_callback; ++i)
      if (finalize_callback[i] == callback) return;

   finalize_callback = realloc(finalize_callback, (num_finalize_callback + 1) *
                               sizeof(*finalize_callback));

   finalize_callback[num_finalize_callback++] = callback;
}

void yac_comm_check(struct communicator * comm) {

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_check\n");
#endif

   if (comm->vtable->check == NULL)
      yac_internal_abort_message(
        "ERROR: comm_check not implemented", __FILE__, __LINE__);

   comm->vtable->check(comm);
}

void yac_comm_finalize() {

#ifdef YAC_DEBUG_COMMUNICATION
   fprintf(stderr, "communicator comm_finalize\n");
#endif

   for (int i = 0; i < num_finalize_callback; ++i)
      finalize_callback[i]();

   free(finalize_callback);

   finalize_callback = NULL;
   num_finalize_callback = 0;
}

static void comm_op_sum_uint(void * a, void * b) {
  *(unsigned*)b = *(unsigned*)a + *(unsigned*)b;
}
static void comm_op_sum_int(void * a, void * b) {
  *(int*)b = *(int*)a + *(int*)b;
}
static void comm_op_sum_dble(void * a, void * b) {
  *(double*)b = *(double*)a + *(double*)b;
}
static void comm_op_sum_byte(void * a, void * b) {
  *(char*)b = (char)(*(char*)a + *(char*)b);
}

static void comm_op_sum_char(void * a, void * b) {
  *(char*)b = (char)(*(char*)a + *(char*)b);
}

static void comm_op_sum(void * invec, void * inoutvec, unsigned count,
                        enum yac_comm_data_type type) {

  struct {
    size_t size;
    void (*func)(void * a, void * b);
  } comm_op_sum[] = {{.size = sizeof(unsigned), .func = comm_op_sum_uint},
                     {.size = sizeof(int),      .func = comm_op_sum_int},
                     {.size = sizeof(double),   .func = comm_op_sum_dble},
                     {.size = sizeof(char),     .func = comm_op_sum_byte},
                     {.size = sizeof(char),     .func = comm_op_sum_char}},
    this_comm_op_sum;

  switch (type) {
    default : yac_internal_abort_message("ERROR(comm_op_sum): unsupported or "
                                         "invalid data type", __FILE__, __LINE__);
      break;
    case (COMM_UINT): this_comm_op_sum = comm_op_sum[0]; break;
    case (COMM_INT): this_comm_op_sum = comm_op_sum[1]; break;
    case (COMM_DBLE): this_comm_op_sum = comm_op_sum[2]; break;
    case (COMM_BYTE): this_comm_op_sum = comm_op_sum[3]; break;
    case (COMM_CHAR): this_comm_op_sum = comm_op_sum[4]; break;
  };

  for (unsigned i = 0; i < count; ++i) {
    this_comm_op_sum.func(invec, inoutvec);
    invec = (char*)invec + this_comm_op_sum.size;
    inoutvec = (char*)inoutvec + this_comm_op_sum.size;
  }
}

static void comm_op_or_uint(void * a, void * b) {
  *(unsigned*)b = *(unsigned*)a | *(unsigned*)b;
}
static void comm_op_or_int(void * a, void * b) {
  *(int*)b = *(int*)a | *(int*)b;
}
static void comm_op_or_byte(void * a, void * b) {
  *(char*)b = (char)(*(char*)a | *(char*)b);
}
static void comm_op_or_char(void * a, void * b) {
  *(char*)b = (char)(*(char*)a | *(char*)b);
}

static void comm_op_or(void * invec, void * inoutvec, unsigned count,
                       enum yac_comm_data_type type) {

  struct {
    size_t size;
    void (*func)(void * a, void * b);
  } comm_op_or[] = {{.size = sizeof(unsigned), .func = comm_op_or_uint},
                    {.size = sizeof(int),      .func = comm_op_or_int},
                    {.size = sizeof(char),     .func = comm_op_or_byte},
                    {.size = sizeof(char),     .func = comm_op_or_char}},
    this_comm_op_or;

  switch (type) {
    default : yac_internal_abort_message("ERROR(comm_op_or): unsupported or "
                                         "invalid data type", __FILE__, __LINE__);
      break;
    case (COMM_UINT): this_comm_op_or = comm_op_or[0]; break;
    case (COMM_INT): this_comm_op_or = comm_op_or[1]; break;
    case (COMM_BYTE): this_comm_op_or = comm_op_or[2]; break;
    case (COMM_CHAR): this_comm_op_or = comm_op_or[3]; break;
  };

  for (unsigned i = 0; i < count; ++i) {
    this_comm_op_or.func(invec, inoutvec);
    invec = (char*)invec + this_comm_op_or.size;
    inoutvec = (char*)inoutvec + this_comm_op_or.size;
  }
}

func_comm_op COMM_OP_SUM = comm_op_sum;
func_comm_op COMM_OP_OR  = comm_op_or;

