/*============================================================================
 * Main structure for handling of interfaces associating mesh entities
 * (such as inter-processor or periodic connectivity between cells, faces,
 * or vertices);
 *============================================================================*/

/*
  This file is part of the "Finite Volume Mesh" library, intended to provide
  finite volume mesh and associated fields I/O and manipulation services.

  Copyright (C) 2006  EDF

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*----------------------------------------------------------------------------
 * Standard C library headers
 *----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------
 * BFT library headers
 *----------------------------------------------------------------------------*/

#include <bft_mem.h>
#include <bft_printf.h>

/*----------------------------------------------------------------------------
 *  Local headers
 *----------------------------------------------------------------------------*/

#include <fvm_defs.h>
#include <fvm_config_defs.h>
#include <fvm_order.h>
#include <fvm_parall.h>

/*----------------------------------------------------------------------------
 *  Header for the current file
 *----------------------------------------------------------------------------*/

#include <fvm_interface.h>

/*----------------------------------------------------------------------------*/

#ifdef __cplusplus
extern "C" {
#if 0
} /* Fake brace to force back Emacs auto-indentation back to column 0 */
#endif
#endif /* __cplusplus */

/*============================================================================
 * Local structure definitions
 *============================================================================*/

/*----------------------------------------------------------------------------
 * Local structure defining a temporary list of interfaces
 *----------------------------------------------------------------------------*/

struct _fvm_interface_t {

  int          rank;         /* Associated rank */

  fvm_lnum_t   size;         /* Number of equivalent elements */
  fvm_lnum_t  *local_num;    /* Local element numbers */
  fvm_lnum_t  *distant_num;  /* Distant element numbers */

};

/*----------------------------------------------------------------------------
 * Structure defining a set of interfaces
 *----------------------------------------------------------------------------*/

struct _fvm_interface_set_t {

  int                size;        /* Number of interfaces*/

  fvm_interface_t  **interfaces;  /* Pointers to interface structures */

};

/*----------------------------------------------------------------------------
 * Local structure defining a temporary list of interfaces
 *----------------------------------------------------------------------------*/

typedef struct {

  int          count;    /* Number of equivalences */
  int         *multiple; /* Count of equivalent elements for each equivalence */
  int         *shift;    /* Index of per-equivalence data in rank[] and num[] */
  int         *rank;     /* Rank associated with each element */
  fvm_lnum_t  *num;      /* Local number associated with each element */

} _per_slice_equiv_t;

/*=============================================================================
 * Private function definitions
 *============================================================================*/

#if defined(FVM_HAVE_MPI)

/*----------------------------------------------------------------------------
 * Creation of an empty interface between entities of a same type.
 *
 * This interface may be used to identify equivalent vertices or faces using
 * domain splitting, as well as periodic entities (on the same or on
 * distant ranks).
 *
 * returns:
 *  pointer to allocated interface structure
 *----------------------------------------------------------------------------*/

static fvm_interface_t *
_fvm_interface_create(void)
{
  fvm_interface_t  *_interface;

  BFT_MALLOC(_interface, 1, fvm_interface_t);

  _interface->rank = -1;
  _interface->size = 0;

  _interface->local_num = NULL;
  _interface->distant_num = NULL;

  return _interface;
}

#endif /* defined (FVM_HAVE_MPI) */

/*----------------------------------------------------------------------------
 * Destruction of an interface.
 *
 * parameters:
 *   this_interface <-- pointer to structure that should be destroyed
 *
 * returns:
 *   NULL pointer
 *----------------------------------------------------------------------------*/

static fvm_interface_t *
_fvm_interface_destroy(fvm_interface_t  *this_interface)
{
  if (this_interface != NULL) {
    BFT_FREE(this_interface->local_num);
    BFT_FREE(this_interface->distant_num);
    BFT_FREE(this_interface);
  }

  return this_interface;
}

/*----------------------------------------------------------------------------
 * Dump printout of an interface.
 *
 * parameters:
 *   this_interface <-- pointer to structure that should be dumped
 *----------------------------------------------------------------------------*/

static void
_fvm_interface_dump(const fvm_interface_t  *this_interface)
{
  int i;

  if (this_interface == NULL) {
    bft_printf(_("  interface: nil\n"));
    return;
  }

  bft_printf(_("  interface:       %p\n"
               "  associated rank: %d\n"
               "  size:            %d\n\n"
               "            id      local    distant\n"),
             this_interface,
             this_interface->rank, this_interface->size);

  if (this_interface->distant_num != NULL) {
    for (i = 0; i < this_interface->size; i++)
      bft_printf(_("    %10d %10d %10d\n"), i,
                 this_interface->local_num[i],
                 this_interface->distant_num[i]);
  }
  else {
    for (i = 0; i < this_interface->size; i++)
      bft_printf(_("    %10d %10d\n"), i,
                 this_interface->local_num[i]);
  }

  bft_printf("\n");
}

#if defined(FVM_HAVE_MPI)

/*----------------------------------------------------------------------------
 * Maximum global number associated with an I/O numbering structure
 *
 * parameters:
 *   n_ent      <-- local number of entities
 *   global_num <-- global number (id) associated with each entity
 *   comm       <-- associated MPI communicator
 *
 * returns:
 *   maximum global number associated with the I/O numbering
 *----------------------------------------------------------------------------*/

static fvm_gnum_t
_global_num_max(fvm_lnum_t        n_ent,
                const fvm_gnum_t  global_num[],
                MPI_Comm          comm)
{
  fvm_gnum_t  local_max, global_max;

  /* Get maximum global number value */

  if (n_ent > 0)
    local_max = global_num[n_ent - 1];
  else
    local_max = 0;

  MPI_Allreduce(&local_max, &global_max, 1, FVM_MPI_GNUM, MPI_MAX, comm);

  return global_max;
}

/*----------------------------------------------------------------------------
 * Build temporary equivalence structure for data in a given slice,
 * and associate an equivalence id to received elements (-1 for elements
 * with no correponding elements)
 *
 * parameters:
 *   n_ranks         <-- number of associateed ranks
 *   n_ent_recv      <-- number of entities received
 *   recv_shift      <-- shift in reveived data per rank (size: n_ranks+1)
 *   recv_global_num <-- global numbering received
 *   recv_num        <-- local numbering received
 *   equiv_id        --> equivalence id for each element (-1 if none)
 *----------------------------------------------------------------------------*/

static _per_slice_equiv_t
_slice_global_num_to_equiv(int                n_ranks,
                           fvm_lnum_t         n_ent_recv,
                           const fvm_lnum_t   recv_shift[],
                           const fvm_gnum_t   recv_global_num[],
                           const fvm_lnum_t   recv_num[],
                           fvm_lnum_t         equiv_id[])
{
  fvm_lnum_t  i, j;
  int         rank;
  fvm_gnum_t  cur_num, prev_num;

  fvm_lnum_t  *recv_order = NULL;

  _per_slice_equiv_t  e;

  /* Initialize return structure */

  e.count    = 0;
  e.multiple = NULL;
  e.shift    = NULL;
  e.rank     = NULL;
  e.num      = NULL;

  /* Determine equivalent elements; requires ordering to loop through buffer
     by increasing number (slice blocks associated with each rank are
     already sorted, but the whole "gathered" slice is not). */

  BFT_MALLOC(recv_order, n_ent_recv, fvm_lnum_t);

  fvm_order_local_allocated(NULL,
                            recv_global_num,
                            recv_order,
                            n_ent_recv);

  /* Loop by increasing number: if two elements have the same global
     number, they are equivalent. We do not increment equivalence counts
     as soon as two elements are equivalent, as three equivalent elements
     should have the same equivalence id. Rather, we increment the
     equivalence counter when the previous element was part of an
     equivalence and the current element is not part of this same
     equivalence. */

  e.count = 0;

  equiv_id[recv_order[0]] = -1;
  prev_num = recv_global_num[recv_order[0]];

  for (i = 1; i < n_ent_recv; i++) {
    cur_num = recv_global_num[recv_order[i]];
    if (cur_num == prev_num) {
      equiv_id[recv_order[i-1]] = e.count;
      equiv_id[recv_order[i]]   = e.count;
    }
    else {
      if (equiv_id[recv_order[i-1]] > -1)
        e.count++;
      equiv_id[recv_order[i]] = -1;
    }
    prev_num = cur_num;
  }
  if (equiv_id[recv_order[n_ent_recv-1]] > -1)
    e.count++;

  BFT_FREE(recv_order);

  /* Count number of elements associated with each equivalence */

  BFT_MALLOC(e.multiple, e.count, int);
  BFT_MALLOC(e.shift, e.count+1, fvm_lnum_t);

  for (i = 0; i < e.count; e.multiple[i++] = 0);
  for (i = 0; i < n_ent_recv; i++) {
    if (equiv_id[i] > -1)
      e.multiple[equiv_id[i]] += 1;
  }

  e.shift[0] = 0;
  for (i = 0; i < e.count; i++)
    e.shift[i+1] = e.shift[i] + e.multiple[i];

  /* Build equivalence data */

  BFT_MALLOC(e.rank, e.shift[e.count], fvm_lnum_t);
  BFT_MALLOC(e.num, e.shift[e.count], fvm_lnum_t);

  for (i = 0; i < e.count; e.multiple[i++] = 0);

  for (rank = 0; rank < n_ranks; rank++) {
    for (i = recv_shift[rank]; i < recv_shift[rank+1]; i++) {
      if (equiv_id[i] > -1) {
        j = e.shift[equiv_id[i]] + e.multiple[equiv_id[i]];
        e.rank[j] = rank;
        e.num[j] = recv_num[i];
        e.multiple[equiv_id[i]] += 1;
      }
    }
  }

  return e;
}

/*----------------------------------------------------------------------------
 * Build global interface data from flat equivalence data
 * (usually prepared and received from distant ranks).
 *
 * parameters:
 *   this_interface_set  <-> pointer to structure that should be updated
 *   n_ent_recv          <-- size of received data
 *   equiv_recv          <-- flat (received) equivalence data; for each
 *                           equivalence, we have:
 *                           {local_number, n_equivalents,
 *                            {distant_number, distant_rank}*n_equivalents}
 *----------------------------------------------------------------------------*/

static void
_interfaces_from_flat_equiv(fvm_interface_set_t  *this_interface_set,
                            fvm_lnum_t            n_ent_recv,
                            const fvm_lnum_t      equiv_recv[])
{
  fvm_lnum_t  i, j, k;
  fvm_lnum_t  local_num, distant_num, n_sub;
  int rank;

  int max_rank = 0, n_ranks = 0, start_id = 0;

  fvm_lnum_t  *n_ent_rank = NULL;
  int *interface_id = NULL;

  fvm_interface_t *_interface = NULL;

  /* Compute size of subsections for each rank */

  i = 0;
  while (i < n_ent_recv) {
    i++;
    n_sub = equiv_recv[i++];
    for (j = 0; j < n_sub; j++) {
      i++;
      rank = equiv_recv[i++];
      if (rank > max_rank)
        max_rank = rank;
    }
  }

  BFT_MALLOC(n_ent_rank, max_rank + 1, fvm_lnum_t);

  for (i = 0; i < max_rank + 1; n_ent_rank[i++] = 0);

  i = 0;
  while (i < n_ent_recv) {
    i++;
    n_sub = equiv_recv[i++];
    for (j = 0; j < n_sub; j++) {
      i++;
      rank = equiv_recv[i++];
      n_ent_rank[rank] += 1;
    }
  }

  /* Build final data structures */

  n_ranks = 0;
  for (i = 0; i < max_rank + 1; i++) {
    if (n_ent_rank[i] > 0)
      n_ranks++;
  }

  /* (Re-)Allocate structures */

  start_id = this_interface_set->size;

  this_interface_set->size += n_ranks;

  BFT_REALLOC(this_interface_set->interfaces,
              this_interface_set->size,
              fvm_interface_t *);

  for (i = start_id; i < this_interface_set->size; i++)
    this_interface_set->interfaces[i] = _fvm_interface_create();

  /* Initialize rank info and interface id */

  n_ranks = 0;
  BFT_MALLOC(interface_id, max_rank + 1, int);
  for (i = 0; i < max_rank + 1; i++) {
    if (n_ent_rank[i] > 0) {
      interface_id[i] = start_id + n_ranks++;
      (this_interface_set->interfaces[interface_id[i]])->rank = i;
      (this_interface_set->interfaces[interface_id[i]])->size = n_ent_rank[i];
    }
    else
      interface_id[i] = -1;
  }

  /* n_ent_rank will now be used as a position counter for new interfaces */

  BFT_REALLOC(n_ent_rank,
              this_interface_set->size - start_id,
              fvm_lnum_t);

  for (i = start_id; i < this_interface_set->size; i++) {

    n_ent_rank[i - start_id] = 0;

    _interface = this_interface_set->interfaces[i];

    BFT_MALLOC(_interface->local_num, _interface->size, fvm_lnum_t);
    BFT_MALLOC(_interface->distant_num, _interface->size, fvm_lnum_t);

  }

  /* Now populate the arrays */

  i = 0;
  while (i < n_ent_recv) {

    local_num = equiv_recv[i++];
    n_sub = equiv_recv[i++];

    for (j = 0; j < n_sub; j++) {

      distant_num = equiv_recv[i++];
      rank = equiv_recv[i++];

      _interface = this_interface_set->interfaces[interface_id[rank]];
      k = interface_id[rank] - start_id;

      _interface->local_num[n_ent_rank[k]] = local_num;
      _interface->distant_num[n_ent_rank[k]] = distant_num;
      n_ent_rank[k] += 1;

    }
  }

  /* n_ent_rank will now be used as a position counter for new interfaces */

  BFT_FREE(n_ent_rank);
  BFT_FREE(interface_id);
}

/*----------------------------------------------------------------------------
 * Global ordering associated with an I/O numbering structure.
 *
 * The global_num values should be sorted, but need not be contiguous.
 *
 * parameters:
 *   this_interface_set  <-> pointer to structure that should be updated
 *   n_ent               <-- local number of entities
 *   global_num          <-- global number (id) associated with each entity
 *   comm                <-- associated MPI communicator
 *----------------------------------------------------------------------------*/

static void
_fvm_interface_add_global_equiv(fvm_interface_set_t  *this_interface_set,
                                fvm_lnum_t            n_ent,
                                fvm_gnum_t            global_num[],
                                MPI_Comm              comm)
{
  fvm_gnum_t  global_max;
  fvm_lnum_t  i, j, n_ent_recv, n_ent_send;
  size_t      slice_size;
  int         size, rank;

  _per_slice_equiv_t  e;

  int         *send_count = NULL, *recv_count = NULL;
  int         *send_shift = NULL, *recv_shift = NULL;
  fvm_lnum_t  *equiv_id = NULL;
  fvm_lnum_t  *equiv_send, *equiv_recv = NULL;
  fvm_lnum_t  *recv_num = NULL, *send_num = NULL;
  fvm_gnum_t  *recv_global_num = NULL;

  /* Initialization */

  MPI_Comm_size(comm, &size);

  /* Get temporary maximum global number value */

  global_max = _global_num_max(n_ent,
                               global_num,
                               comm);

  /* slice_size = ceil(global_max/size) */

  slice_size = global_max / size;
  if (global_max % size > 0)
    slice_size += 1;

  assert(sizeof(fvm_gnum_t) >= sizeof(fvm_lnum_t));

  BFT_MALLOC(send_count, size, int);
  BFT_MALLOC(recv_count, size, int);

  BFT_MALLOC(send_shift, size + 1, int);
  BFT_MALLOC(recv_shift, size + 1, int);

  /* Count number of values to send to each process */

  for (rank = 0; rank < size; rank++)
    send_count[rank] = 0;

  for (i = 0; i < n_ent; i++)
    send_count[(global_num[i] - 1) / slice_size] += 1;

  MPI_Alltoall(send_count, 1, FVM_MPI_GNUM, recv_count, 1, FVM_MPI_GNUM, comm);

  send_shift[0] = 0;
  recv_shift[0] = 0;

  for (rank = 0; rank < size; rank++) {
    send_shift[rank + 1] = send_shift[rank] + send_count[rank];
    recv_shift[rank + 1] = recv_shift[rank] + recv_count[rank];
  }

  /* As data is sorted by increasing base global numbering, we do not
     need to build an extra array, but only to send the correct parts
     of the global_num[] array to the correct processors */

  n_ent_recv = recv_shift[size];

  BFT_MALLOC(recv_global_num, n_ent_recv, fvm_gnum_t);
  BFT_MALLOC(recv_num, n_ent_recv, fvm_lnum_t);

  MPI_Alltoallv(global_num, send_count, send_shift, FVM_MPI_GNUM,
                recv_global_num, recv_count, recv_shift, FVM_MPI_GNUM, comm);


  /* We also need to send the corresponding local numbers */

  n_ent_send = send_shift[size];

  BFT_MALLOC(send_num, n_ent_send, fvm_lnum_t);

  for (i = 0; i < n_ent_send; i++)
    send_num[i] = i+1;

  MPI_Alltoallv(send_num, send_count, send_shift, FVM_MPI_LNUM,
                recv_num, recv_count, recv_shift, FVM_MPI_LNUM, comm);

  BFT_FREE(send_num);

  if (n_ent_recv > 0) {

    /* Build equivalence data */

    BFT_MALLOC(equiv_id, n_ent_recv, fvm_lnum_t);

    e = _slice_global_num_to_equiv(size,
                                   n_ent_recv,
                                   recv_shift,
                                   recv_global_num,
                                   recv_num,
                                   equiv_id);

  }

  /* Now free Memory */

  BFT_FREE(recv_num);
  BFT_FREE(recv_global_num);

  /* Now that equivalences are marked, count for each rank; for each
     equivalence, we will need to send the local entity number,
     the number of equivalent entities (e.multiple[...] - 1),
     and the corresponding entity numbers and ranks,
     for a total of 1 + 1 + 2*(e.multiple[...] - 1)
     = 2*(e.multiple[...]) values. */

  for (rank = 0; rank < size; rank++) {
    send_count[rank] = 0;
    for (i = recv_shift[rank]; i < recv_shift[rank+1]; i++) {
      if (equiv_id[i] > -1) {
        send_count[rank] += (2*e.multiple[equiv_id[i]]);
      }
    }
  }

  for (rank = 0; rank < size; rank++)
    send_shift[rank + 1] = send_shift[rank] + send_count[rank];

  /* Now prepare new send buffer */

  n_ent_send = send_shift[size];
  BFT_MALLOC(equiv_send, n_ent_send, fvm_lnum_t);

  for (rank = 0; rank < size; rank++) {

    send_count[rank] = 0; /* reset, will be re-incremented */

    for (i = recv_shift[rank]; i < recv_shift[rank+1]; i++) {

      if (equiv_id[i] > -1) {

        fvm_lnum_t *equiv_send_p
          = equiv_send + send_shift[rank] + send_count[rank];

        fvm_lnum_t  e_id = equiv_id[i];
        fvm_lnum_t  k = 2;
        const int        *rank_p = e.rank + e.shift[e_id];
        const fvm_lnum_t *num_p  = e.num  + e.shift[e_id];

        send_count[rank] += (2*e.multiple[equiv_id[i]]);

        for (j = 0; j < e.multiple[e_id]; j++) {
          if (rank_p[j] == rank) {
            equiv_send_p[0] = num_p[j];
            equiv_send_p[1] = e.multiple[e_id] - 1;
          }
          else {
            equiv_send_p[k++] = num_p[j];
            equiv_send_p[k++] = rank_p[j];
          }
        }

      }
    }
  }

  /* Free temporary (slice) equivalence info */

  e.count = 0;
  BFT_FREE(e.multiple);
  BFT_FREE(e.shift);
  BFT_FREE(e.rank);
  BFT_FREE(e.num);

  BFT_FREE(equiv_id);

  /* Send prepared slice data to destination rank */

  MPI_Alltoall(send_count, 1, FVM_MPI_GNUM, recv_count, 1, FVM_MPI_GNUM, comm);

  send_shift[0] = 0;
  recv_shift[0] = 0;

  for (rank = 0; rank < size; rank++) {
    send_shift[rank + 1] = send_shift[rank] + send_count[rank];
    recv_shift[rank + 1] = recv_shift[rank] + recv_count[rank];
  }

  n_ent_recv = recv_shift[size];

  BFT_MALLOC(equiv_recv, n_ent_recv, fvm_lnum_t);

  MPI_Alltoallv(equiv_send, send_count, send_shift, FVM_MPI_LNUM,
                equiv_recv, recv_count, recv_shift, FVM_MPI_LNUM, comm);

  /* At this stage, MPI operations are finished; we may release
     the corresponding counts and indexes */

  BFT_FREE(equiv_send);

  BFT_FREE(send_count);
  BFT_FREE(recv_count);
  BFT_FREE(send_shift);
  BFT_FREE(recv_shift);

  /* Add interface */

  _interfaces_from_flat_equiv(this_interface_set,
                              n_ent_recv,
                              equiv_recv);

  BFT_FREE(equiv_recv);
}

#endif /* defined (FVM_HAVE_MPI) */

/*=============================================================================
 * Public function definitions
 *============================================================================*/

/*----------------------------------------------------------------------------
 * Return process rank associated with an interface's distant entities.
 *
 * parameters:
 *   this_interface <-- pointer to interface structure
 *
 * returns:
 *   process rank associated with the interface's distant entities
 *----------------------------------------------------------------------------*/

int
fvm_interface_rank(const fvm_interface_t  *this_interface)
{
  int retval = -1;

  if (this_interface != NULL)
    retval = this_interface->rank;

  return retval;
}

/*----------------------------------------------------------------------------
 * Return number of local and distant entities defining an interface.
 *
 * parameters:
 *   this_interface <-- pointer to interface structure
 *
 * returns:
 *   number of local and distant entities defining the interface
 *----------------------------------------------------------------------------*/

fvm_lnum_t
fvm_interface_size(const fvm_interface_t  *this_interface)
{
  fvm_lnum_t retval = 0;

  if (this_interface != NULL)
    retval = this_interface->size;

  return retval;
}

/*----------------------------------------------------------------------------
 * Return pointer to array of local entity numbers defining an interface.
 *
 * The size of the array may be obtained by fvm_interface_size().
 * The array is owned by the interface structure, and is not copied
 * (hence the constant qualifier for the return value).
 *
 * parameters:
 *   this_interface <-- pointer to interface structure
 *
 * returns:
 *   pointer to array of local entity numbers defining the interface
 *----------------------------------------------------------------------------*/

const fvm_lnum_t *
fvm_interface_get_local_num(const fvm_interface_t  *this_interface)
{
  const fvm_lnum_t *retval = 0;

  if (this_interface != NULL)
    retval = this_interface->local_num;

  return retval;
}

/*----------------------------------------------------------------------------
 * Return pointer to array of distant entity numbers defining an interface.
 *
 * The size of the array may be obtained by fvm_interface_size().
 * The array is owned by the interface structure, and is not copied
 * (hence the constant qualifier for the return value).
 *
 * parameters:
 *   this_interface <-- pointer to interface structure
 *
 * returns:
 *   pointer to array of local entity numbers defining the interface
 *----------------------------------------------------------------------------*/

const fvm_lnum_t *
fvm_interface_get_distant_num(const fvm_interface_t  *this_interface)
{
  const fvm_lnum_t *retval = 0;

  if (this_interface != NULL)
    retval = this_interface->distant_num;

  return retval;
}

/*----------------------------------------------------------------------------
 * Creation of a list of interfaces between entities of a same type.
 *
 * These interfaces may be used to identify equivalent vertices or faces using
 * domain splitting, as well as periodic entities (on the same or on
 * distant ranks).
 *
 * The corresponding entities must be locally ordered.
 *
 * parameters:
 *   n_ent                <-- number of entities considered
 *   parent_entity_number <-- pointer to list of selected entitie's parent's
 *                            numbers, or NULL if all first n_ent entities
 *                            are used
 *   parent_global_number <-- pointer to list of global (i.e. domain splitting
 *                            independent) parent entity numbers
 *
 * returns:
 *  pointer to list of interfaces (possibly NULL in serial mode)
 *----------------------------------------------------------------------------*/

fvm_interface_set_t *
fvm_interface_set_create(fvm_lnum_t        n_ent,
                         const fvm_lnum_t  parent_entity_number[],
                         const fvm_gnum_t  parent_global_number[])
{
  fvm_interface_set_t  *this_interface_set;

  /* Initial checks */

  assert(   fvm_order_local_test(parent_entity_number,
                                 parent_global_number,
                                 n_ent) == true
         || parent_global_number == NULL);

  if (fvm_parall_get_size() < 2)
    return NULL;

#if defined(FVM_HAVE_MPI)

 {
   size_t  i;
   fvm_gnum_t  *global_num = NULL;

   /* Create structure */

   BFT_MALLOC(this_interface_set, 1, fvm_interface_set_t);
   this_interface_set->size = 0;
   this_interface_set->interfaces = NULL;

   if (n_ent > 0) {

     BFT_MALLOC(global_num, n_ent, fvm_gnum_t);

     /* Assign initial global numbers */

     if (parent_entity_number != NULL) {
       for (i = 0 ; i < (size_t)n_ent ; i++)
         global_num[i] = parent_global_number[parent_entity_number[i]-1];
     }
     else {
       for (i = 0 ; i < (size_t)n_ent ; i++)
         global_num[i] = parent_global_number[i];
     }

   }

   /* Build interfaces */

   _fvm_interface_add_global_equiv(this_interface_set,
                                   n_ent,
                                   global_num,
                                   fvm_parall_get_mpi_comm());

   BFT_FREE(global_num);

 }
#endif

  return this_interface_set;
}

/*----------------------------------------------------------------------------
 * Destruction of an interface list.
 *
 * parameters:
 *   this_interface_set <-- pointer to structure that should be destroyed
 *
 * returns:
 *   NULL pointer
 *----------------------------------------------------------------------------*/

fvm_interface_set_t *
fvm_interface_set_destroy(fvm_interface_set_t  *this_interface_set)
{
  int i;

  if (this_interface_set != NULL) {
    for (i = 0; i < this_interface_set->size; i++) {
      _fvm_interface_destroy(this_interface_set->interfaces[i]);
    }
    BFT_FREE(this_interface_set->interfaces);
    BFT_FREE(this_interface_set);
  }

  return this_interface_set;
}

/*----------------------------------------------------------------------------
 * Return number of interfaces associated with an interface set.
 *
 * parameters:
 *   this_interface_set <-- pointer to interface set structure
 *
 * returns:
 *   number of interfaces in set
 *----------------------------------------------------------------------------*/

int
fvm_interface_set_size(const fvm_interface_set_t  *this_interface_set)
{
  int retval = 0;

  if (this_interface_set != NULL)
    retval = this_interface_set->size;

  return retval;
}

/*----------------------------------------------------------------------------
 * Return pointer to a given interface in an interface set.
 *
 * parameters:
 *   this_interface_set <-- pointer to interface set structure
 *   interface_id       <-- index of interface in set (0 to n-1)
 *
 * returns:
 *   pointer to interface structure
 *----------------------------------------------------------------------------*/

const fvm_interface_t *
fvm_interface_set_get_interface(const fvm_interface_set_t  *this_interface_set,
                                int                         interface_id)
{
  const fvm_interface_t  *retval = NULL;

  if (this_interface_set != NULL) {
    if (interface_id > -1 && interface_id < this_interface_set->size)
      retval = this_interface_set->interfaces[interface_id];
  }

  return retval;
}

/*----------------------------------------------------------------------------
 * Dump printout of an interface list.
 *
 * parameters:
 *   this_interface_set <-- pointer to structure that should be dumped
 *----------------------------------------------------------------------------*/

void
fvm_interface_set_dump(const fvm_interface_set_t  *this_interface_set)
{
  int i;

  if (this_interface_set == NULL) {
    bft_printf(_("  interface list: nil\n"));
    return;
  }

  bft_printf(_("  interface list: %p\n"
               "  n interfaces:   %d\n"),
             this_interface_set, this_interface_set->size);

  for (i = 0; i < this_interface_set->size; i++) {
    bft_printf(_("\n  interface %d:\n"), i);
    _fvm_interface_dump(this_interface_set->interfaces[i]);
  }

}

/*----------------------------------------------------------------------------*/

#ifdef __cplusplus
}
#endif /* __cplusplus */


syntax highlighted by Code2HTML, v. 0.9.1