/*
 * Copyright (c) 2005 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Governement
 * retains certain rights in this software.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 * 
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.  
 * 
 *     * Neither the name of Sandia Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */
/*****************************************************************************
*
* exgssn - ex_get_side_set_node_list
*
* entry conditions - 
*   input parameters:
*       int     exoid                   exodus file id
*       int     side_set_id             side set id
*
* exit conditions - 
*       int     *side_set_node_cnt_list returned array of number of nodes for
*                                       side or face
*       int     *side_set_node_list     array of nodes
*
* revision history - 
*
*
*****************************************************************************/

#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "exodusII.h"
#include "exodusII_int.h"
/*!
 * This routine is designed to read the Exodus II V 2.0 side set side 
 * definition  and return a ExodusI style side set node definition.
 */
static void* safe_free(void* array)
{
  if (array != 0) free(array);
  return 0;
}

static void set_count(int exoid, void_int *cnt, size_t ndx, size_t val)
{
  if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
    ((int64_t*)cnt)[ndx] = val;
  } else {
    ((int*)cnt)[ndx] = val;
  }
}

static void get_nodes(int exoid, void_int *to, size_t ito, void_int *from, size_t ifrom)
{
  if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
    ((int64_t*)to)[ito] = ((int64_t*)from)[ifrom];
  } else {
    ((int*)to)[ito] = ((int*)from)[ifrom];
  }
}

int ex_get_side_set_node_list(int exoid,
			      ex_entity_id side_set_id,
			      void_int *side_set_node_cnt_list,
			      void_int *side_set_node_list)
{
  size_t m;
  size_t i, j; 
  int64_t elem, side;
  int64_t num_side_sets, num_elem_blks, num_df, ndim;
  int64_t tot_num_elem = 0, tot_num_ss_elem = 0, elem_num = 0;
  size_t connect_offset, side_num, node_pos;
  void_int *elem_blk_ids = NULL;
  void_int *connect = NULL; 
  void_int *ss_elem_ndx = NULL;
  void_int *ss_elem_node_ndx = NULL;
  void_int *ss_parm_ndx = NULL;
  void_int *side_set_elem_list = NULL;
  void_int *side_set_side_list = NULL;
  size_t elem_ctr, node_ctr, elem_num_pos;
  size_t num_nodes_per_elem;
  int int_size, ids_size;

  int err_stat = EX_NOERR;
  int status;

  struct elem_blk_parm  *elem_blk_parms = NULL;

  /* side to node translation tables - 
     These tables are used to look up the side number based on the
     first and second node in the side/face list. The side node order
     is found in the original Exodus document, SAND87-2997. The element
     node order is found in the ExodusII document, SAND92-2137. These
     tables were generated by following the right-hand rule for determining
     the outward normal.
  */
  /* triangle */
  static int tri_table[3][3] = {
    /*   1        2        3                                             side   */
    {1,2,4}, {2,3,5}, {3,1,6}                                       /* nodes  */
  };

  /* triangle 3d */
  static int tri3_table[5][7] = {
    /*       1                2                                          side   */
    {1,2,3,4,5,6,7}, {3,2,1,6,5,4,7},                               /* nodes  */
    /*       3              4              5                             side   */
    {1,2,4,0,0,0,0}, {2,3,5,0,0,0,0}, {3,1,6,0,0,0,0}               /* nodes  */
  };

  /* quad */
  static int quad_table[4][3] = {
    /*   1        2        3        4                                    side   */
    {1,2,5}, {2,3,6}, {3,4,7}, {4,1,8}                              /* nodes  */
  };

  /* shell */
  static int shell_table[6][8] = {
    /*        1                  2                                       side   */
    {1,2,3,4,5,6,7,8}, {1,4,3,2,8,7,6,5} ,                          /* nodes  */
    /*        3                  4                                       side   */
    {1,2,5,0,0,0,0,0}, {2,3,6,0,0,0,0,0} ,                          /* nodes  */
    /*        5                  6                                       side   */
    {3,4,7,0,0,0,0,0}, {4,1,8,0,0,0,0,0}                            /* nodes  */
  };

  /* tetra */
  static int tetra_table[4][6] = {
    /*      1              2               3               4            side   */
    {1,2,4,5,9,8}, {2,3,4,6,10,9}, {1,4,3,8,10,7}, {1,3,2,7,6,5}   /* nodes  */
  };

  /* wedge */
  static int wedge_table[5][8] = {
    /*        1                     2                     3              side   */
    {1,2,5,4,7,11,13,10}, {2,3,6,5,8,12,14,11}, {1,4,6,3,10,15,12,9},
    /*        4                  5                                       side   */
    {1,3,2,0,9,8,7,0}, {4,5,6,0,13,14,15,0}                         /* nodes  */
  };

  /* hex */
  static int hex_table[6][9] = {
    /*         1                        2                                side   */
    {1,2,6,5,9,14,17,13,26}, {2,3,7,6,10,15,18,14,25},              /* nodes  */
    /*         3                        4                                side   */
    {3,4,8,7,11,16,19,15,27}, {1,5,8,4,13,20,16,12,24},             /* nodes  */
    /*         5                        6                                side   */
    {1,4,3,2,12,11,10,9,22},  {5,6,7,8,17,18,19,20,23}              /* nodes  */
  };

  /* pyramid */
  static int pyramid_table[5][8] = {
    /*          1                   2                    3              side   */
    {1,2,5,0,6,11,10,0}, {2,3,5,0,7,12,11,0}, {3,4,5,0,8,13,12,0}, /* nodes  */
    /*          4                  5                                    side   */
    {1,5,4,0,10,13,9,0}, {1,4,3,2,9,8,7,6}                         /* nodes  */
  };


  char errmsg[MAX_ERR_LENGTH];

  exerrval = 0; /* clear error code */

  /* first check if any side sets are specified */
  /* inquire how many side sets have been stored */

  num_side_sets = ex_inquire_int(exoid, EX_INQ_SIDE_SETS);
  if (num_side_sets < 0) {
    sprintf(errmsg,
	    "Error: failed to get number of side sets in file id %d",exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return(EX_FATAL);
  }

  if (num_side_sets == 0) {
    sprintf(errmsg,
	    "Warning: no side sets defined in file id %d",exoid);
    ex_err("ex_get_side_set_node_list",errmsg,EX_WARN);
    return(EX_WARN);
  }

  /* Lookup index of side set id in VAR_SS_IDS array */
  ex_id_lkup(exoid,EX_SIDE_SET,side_set_id);
  if (exerrval != 0)  {
    if (exerrval == EX_NULLENTITY) {
      sprintf(errmsg,
              "Warning: side set %"PRId64" is NULL in file id %d",
	      side_set_id,exoid);
      ex_err("ex_get_side_set_node_list",errmsg,EX_MSG);
      return (EX_WARN);
    }
    else {

      sprintf(errmsg,
	      "Error: failed to locate side set %"PRId64" in VAR_SS_IDS array in file id %d",
	      side_set_id,exoid);
      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
      return (EX_FATAL);
    }
  }

  num_elem_blks = ex_inquire_int(exoid, EX_INQ_ELEM_BLK);
  if (num_elem_blks < 0) {
    sprintf(errmsg,
	    "Error: failed to get number of element blocks in file id %d",exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return(EX_FATAL);
  }

  tot_num_elem = ex_inquire_int(exoid, EX_INQ_ELEM);
  if (tot_num_elem < 0) {
    sprintf(errmsg,
	    "Error: failed to get total number of elements in file id %d",exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return(EX_FATAL);
  }

  /* get the dimensionality of the coordinates;  this is necessary to
     distinguish between 2d TRIs and 3d TRIs */
  ndim = ex_inquire_int(exoid, EX_INQ_DIM);
  if (ndim < 0) {
    sprintf(errmsg,
	    "Error: failed to get dimensionality in file id %d",exoid);
    ex_err("ex_cvt_nodes_to_sides",errmsg,exerrval);
    return(EX_FATAL);
  }

  int_size = sizeof(int);
  if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
    int_size = sizeof(int64_t);
  }

  ids_size = sizeof(int);
  if (ex_int64_status(exoid) & EX_IDS_INT64_API) {
    ids_size = sizeof(int64_t);
  }

  /* First determine the  # of elements in the side set*/
  if (int_size == sizeof(int64_t)) {
    status = ex_get_set_param(exoid,EX_SIDE_SET, side_set_id,&tot_num_ss_elem,&num_df);
  } else {
    int tot, df;
    status = ex_get_set_param(exoid,EX_SIDE_SET, side_set_id,&tot,&df);
    tot_num_ss_elem = tot;
    num_df = df;
  }

  if (status != EX_NOERR) {
    sprintf(errmsg,
	    "Error: failed to get number of elements in side set %"PRId64" in file id %d",
            side_set_id, exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return(EX_FATAL);
  }

  /* Allocate space for the side set element list */
  if (!(side_set_elem_list=malloc(tot_num_ss_elem*int_size))) {
    exerrval = EX_MEMFAIL;
    sprintf(errmsg,
	    "Error: failed to allocate space for side set element list for file id %d",
	    exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return (EX_FATAL);
  }

  /* Allocate space for the side set side list */
  if (!(side_set_side_list=malloc(tot_num_ss_elem*int_size))) {
    exerrval = EX_MEMFAIL;
    sprintf(errmsg,
	    "Error: failed to allocate space for side set side list for file id %d",
	    exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    err_stat = EX_FATAL;
    goto cleanup;
  }

  if (ex_get_set(exoid, EX_SIDE_SET, side_set_id, 
		 side_set_elem_list, side_set_side_list) == -1) {
    safe_free(side_set_elem_list);
    safe_free(side_set_side_list);
    sprintf(errmsg,
	    "Error: failed to get side set %"PRId64" in file id %d",
	    side_set_id, exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    return (EX_FATAL);
  }

  /* Allocate space for the ss element index array */
  if (!(ss_elem_ndx= malloc(tot_num_ss_elem*int_size))) {
    exerrval = EX_MEMFAIL;
    sprintf(errmsg,
	    "Error: failed to allocate space for side set elem sort array for file id %d",
	    exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    err_stat = EX_FATAL;
    goto cleanup;
  }

  /* Sort side set element list into index array  - non-destructive */
  if (int_size == sizeof(int64_t)) {
    /* Sort side set element list into index array  - non-destructive */
    int64_t *elems = (int64_t*)ss_elem_ndx;
    for (i=0;i<tot_num_ss_elem;i++) {
      elems[i] = i; /* init index array to current position */
    }
    ex_iqsort64(side_set_elem_list, ss_elem_ndx,tot_num_ss_elem);
  } else {
    /* Sort side set element list into index array  - non-destructive */
    int *elems = (int*)ss_elem_ndx;
    for (i=0;i<tot_num_ss_elem;i++) {
      elems[i] = i; /* init index array to current position */
    }
    ex_iqsort(side_set_elem_list, ss_elem_ndx,tot_num_ss_elem);
  }

  /* Allocate space for the element block ids */
  if (!(elem_blk_ids= malloc(num_elem_blks*ids_size))) {
    exerrval = EX_MEMFAIL;
    sprintf(errmsg,
	    "Error: failed to allocate space for element block ids for file id %d",
	    exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    err_stat = EX_FATAL;
    goto cleanup;
  }
  
  if (ex_get_elem_blk_ids(exoid, elem_blk_ids) == -1) {
    sprintf(errmsg,
	    "Error: failed to get element block ids in file id %d",
	    exoid);
    ex_err("ex_get_side_set_node_list",errmsg,EX_MSG);
    err_stat = EX_FATAL;
    goto cleanup;
  }

  /* Allocate space for the element block params */
  if (!(elem_blk_parms= malloc(num_elem_blks*sizeof(struct elem_blk_parm)))) {
    exerrval = EX_MEMFAIL;
    sprintf(errmsg,
	    "Error: failed to allocate space for element block params for file id %d",
            exoid);
    ex_err("ex_get_side_set_node_list",errmsg,exerrval);
    err_stat = EX_FATAL;
    goto cleanup;
  }

  elem_ctr = 0;
  for (i=0; i<num_elem_blks; i++) {
    ex_block block;

    if (ids_size == sizeof(int64_t)) {
      block.id = ((int64_t*)elem_blk_ids)[i];
    } else {
      block.id = ((int*)elem_blk_ids)[i];
    }
    block.type = EX_ELEM_BLOCK;

    /* read in an element block parameter */
    if ((ex_get_block_param (exoid, &block)) == -1) {
      sprintf(errmsg,
	      "Error: failed to get element block %"PRId64" parameters in file id %d",
              block.id, exoid);
      ex_err("ex_get_side_set_node_list",errmsg,EX_MSG);
      err_stat = EX_FATAL;
      goto cleanup;
    }

    elem_blk_parms[i].num_elem_in_blk = block.num_entry;
    elem_blk_parms[i].num_nodes_per_elem = block.num_nodes_per_entry;
    elem_blk_parms[i].num_attr = block.num_attribute;
    elem_blk_parms[i].elem_blk_id = block.id;

    for (m=0; m < strlen(block.topology); m++) {
      elem_blk_parms[i].elem_type[m] = toupper(block.topology[m]);
    }
    elem_blk_parms[i].elem_type[m] = '\0';

    if (strncmp(elem_blk_parms[i].elem_type,"CIRCLE",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_CIRCLE;
	/* set side set node stride */
        elem_blk_parms[i].num_nodes_per_side[0] = 1;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"SPHERE",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_SPHERE;
	/* set side set node stride */
        elem_blk_parms[i].num_nodes_per_side[0] = 1;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"QUAD",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_QUAD;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 4)
	  elem_blk_parms[i].num_nodes_per_side[0] = 2;
	else if (elem_blk_parms[i].num_nodes_per_elem == 5)
	  elem_blk_parms[i].num_nodes_per_side[0] = 2;
	else
	  elem_blk_parms[i].num_nodes_per_side[0] = 3;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"TRIANGLE",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_TRIANGLE;
	/* set default side set node stride */
	if (ndim == 2)  /* 2d TRIs */
	  {
	    if (elem_blk_parms[i].num_nodes_per_elem == 3)
	      elem_blk_parms[i].num_nodes_per_side[0] = 2;
	    else 
	      elem_blk_parms[i].num_nodes_per_side[0] = 3;
	  }
	else if (ndim == 3)  /* 3d TRIs */
	  {
	    if (elem_blk_parms[i].num_nodes_per_elem == 3)
	      elem_blk_parms[i].num_nodes_per_side[0] = 3;
	    else 
	      elem_blk_parms[i].num_nodes_per_side[0] = 6;
	  }
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"SHELL",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_SHELL;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 2) /* KLUDGE for 2D Shells*/
	  elem_blk_parms[i].num_nodes_per_side[0] = 2;
	else if (elem_blk_parms[i].num_nodes_per_elem == 4)
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else
	  elem_blk_parms[i].num_nodes_per_side[0] = 8;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"HEX",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_HEX;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 8)  /* 8-node bricks */
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else if (elem_blk_parms[i].num_nodes_per_elem == 9)  /* 9-node bricks */
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else if (elem_blk_parms[i].num_nodes_per_elem == 12)  /* HEXSHELLS */
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else if (elem_blk_parms[i].num_nodes_per_elem == 27)  /* 27-node bricks */
	  elem_blk_parms[i].num_nodes_per_side[0] = 9;
	else 
	  elem_blk_parms[i].num_nodes_per_side[0] = 8;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"TETRA",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_TETRA;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 4)
	  elem_blk_parms[i].num_nodes_per_side[0] = 3;
	else if (elem_blk_parms[i].num_nodes_per_elem == 8)
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else
	  elem_blk_parms[i].num_nodes_per_side[0] = 6;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"WEDGE",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_WEDGE;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 6)
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else
	  elem_blk_parms[i].num_nodes_per_side[0] = 8;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"PYRAMID",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_PYRAMID;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 5)
	  elem_blk_parms[i].num_nodes_per_side[0] = 4;
	else
	  elem_blk_parms[i].num_nodes_per_side[0] = 8;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"BEAM",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_BEAM;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 2)
	  elem_blk_parms[i].num_nodes_per_side[0] = 2;
	else 
	  elem_blk_parms[i].num_nodes_per_side[0] = 3;
      }
    else if ( (strncmp(elem_blk_parms[i].elem_type,"TRUSS",3) == 0) ||
              (strncmp(elem_blk_parms[i].elem_type,"BAR",3) == 0) ||
              (strncmp(elem_blk_parms[i].elem_type,"EDGE",3) == 0) )
      {
	elem_blk_parms[i].elem_type_val = EX_EL_TRUSS;
	/* determine side set node stride */
	if (elem_blk_parms[i].num_nodes_per_elem == 2)
	  elem_blk_parms[i].num_nodes_per_side[0] = 2;
	else 
	  elem_blk_parms[i].num_nodes_per_side[0] = 3;
      }
    else if (strncmp(elem_blk_parms[i].elem_type,"NULL",3) == 0)
      {
	elem_blk_parms[i].elem_type_val = EX_EL_NULL_ELEMENT;
	elem_blk_parms[i].num_nodes_per_side[0] = 0;
	elem_blk_parms[i].num_elem_in_blk = 0;
      }
    else
      { /* unsupported element type; no problem if no sides specified for
	   this element block */
	elem_blk_parms[i].elem_type_val = EX_EL_UNK;
	elem_blk_parms[i].num_nodes_per_side[0] = 0;
      }
    elem_blk_parms[i].elem_blk_id = block.id;    /* save id */
    elem_ctr += elem_blk_parms[i].num_elem_in_blk;
    elem_blk_parms[i].elem_ctr = elem_ctr;      /* save elem number max */
  }


  /* Allocate space for the ss element to element block parameter index array */
  if (!(ss_parm_ndx=malloc(tot_num_ss_elem*int_size)))
    {
      exerrval = EX_MEMFAIL;
      sprintf(errmsg,
	      "Error: failed to allocate space for side set elem parms index for file id %d",
	      exoid);
      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
      err_stat = EX_FATAL;
      goto cleanup;
    }


  /* Allocate space for the ss element to node list index array */
  if (!(ss_elem_node_ndx=malloc(tot_num_ss_elem*int_size)))
    {
      exerrval = EX_MEMFAIL;
      sprintf(errmsg,
	      "Error: failed to allocate space for side set elem to node index for file id %d",
	      exoid);
      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
           
      err_stat = EX_FATAL;
      goto cleanup;
    }

  /* Build side set element to node list index and side set element 
     parameter index.
  */
  node_ctr = 0;
  for (i=0;i<tot_num_ss_elem;i++) {
    if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
      elem = ((int64_t*)side_set_elem_list)[i];
      side = ((int64_t*)side_set_side_list)[i];
    } else {
      elem = ((int*)side_set_elem_list)[i];
      side = ((int*)side_set_side_list)[i];
    }

    for (j=0; j<num_elem_blks; j++) {
      if (elem_blk_parms[j].elem_type_val != EX_EL_NULL_ELEMENT)
	if (elem <= elem_blk_parms[j].elem_ctr)
	  break;
    }

    if (j >= num_elem_blks) {
      exerrval = EX_BADPARAM;
      sprintf(errmsg,
	      "Error: Invalid element number %"PRId64" found in side set %"PRId64" in file %d",
              elem, side_set_id, exoid);
      ex_err("ex_get_side_set_node_list",errmsg,EX_MSG);
      err_stat = EX_FATAL;
      goto cleanup;
    }

    if (int_size == sizeof(int64_t)) {
      ((int64_t*)ss_parm_ndx)[i] = j; /* assign parameter block index */
      ((int64_t*)ss_elem_node_ndx)[i] = node_ctr;     /* assign node list index */
    } else {
      ((int*)ss_parm_ndx)[i] = j; /* assign parameter block index */
      ((int*)ss_elem_node_ndx)[i] = node_ctr;     /* assign node list index */
    }

    /* Update node_ctr (which points to next node in chain */

    /* WEDGEs with 3 node sides (side 4 or 5) are special cases */
    if (elem_blk_parms[j].elem_type_val == EX_EL_WEDGE &&
        (side == 4 || side == 5))
      {
	if (elem_blk_parms[j].num_nodes_per_elem == 6)
	  node_ctr += 3;  /* 3 node side */
	else
	  node_ctr += 6;  /* 6 node side */
      }
    /* PYRAMIDSs with 3 node sides (sides 1,2,3,4) are also special */
    else if (elem_blk_parms[j].elem_type_val == EX_EL_PYRAMID &&
             (side < 5))
      {
	if (elem_blk_parms[j].num_nodes_per_elem == 5)
	  node_ctr += 3;  /* 3 node side */
	else
	  node_ctr += 6;  /* 6 node side */
      }
    /* side numbers 3,4,5,6 for SHELLs are also special */
    else if (elem_blk_parms[j].elem_type_val == EX_EL_SHELL &&
	     (side > 2 ))
      {
	if (elem_blk_parms[j].num_nodes_per_elem == 4)
	  node_ctr += 2;  /* 2 node side */
	else
	  node_ctr += 3;  /* 3 node side */
      }
    /* side numbers 3,4,5 for 3d TRIs are also special */
    else if (elem_blk_parms[j].elem_type_val == EX_EL_TRIANGLE &&
             ndim == 3 &&
             side > 2 )
      {
	if (elem_blk_parms[j].num_nodes_per_elem == 3)  /* 3-node TRI */
	  node_ctr += 2;  /* 2 node side */
	else   /* 6-node TRI */
	  node_ctr += 3;  /* 3 node side */
      }
    else /* all other element types */
      node_ctr += elem_blk_parms[j].num_nodes_per_side[0];
  }

  /* All setup, ready to go ... */

  elem_ctr=0;

  for (j=0; j < tot_num_ss_elem; j++) {
    int64_t elem_ndx;
    size_t parm_ndx;
    if (ex_int64_status(exoid) & EX_BULK_INT64_API) {
      elem_ndx = ((int64_t*)ss_elem_ndx)[j];
      elem     = ((int64_t*)side_set_elem_list)[elem_ndx];
      side     = ((int64_t*)side_set_side_list)[elem_ndx];
      parm_ndx = ((int64_t*)ss_parm_ndx)[elem_ndx];
    } else {
      elem_ndx = ((int*)ss_elem_ndx)[j];
      elem     = ((int*)side_set_elem_list)[elem_ndx];
      side     = ((int*)side_set_side_list)[elem_ndx];
      parm_ndx = ((int*)ss_parm_ndx)[elem_ndx];
    }
	
    if (elem > elem_ctr) {
      /* release connectivity array space and get next one */
      if (elem_ctr > 0) {
        safe_free(connect);
      }

      /* Allocate space for the connectivity array for new element block */
      if (!(connect=malloc(elem_blk_parms[parm_ndx].num_elem_in_blk*
                           elem_blk_parms[parm_ndx].num_nodes_per_elem*
                           int_size)))
	{
	  exerrval = EX_MEMFAIL;
	  sprintf(errmsg,
		  "Error: failed to allocate space for connectivity array for file id %d",
		  exoid);
	  ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	  err_stat = EX_FATAL;
	  goto cleanup;
	}

      /* get connectivity array */
      if (ex_get_elem_conn(exoid,
			   elem_blk_parms[parm_ndx].elem_blk_id,
			   connect) == -1)
	{
	  sprintf(errmsg,
		  "Error: failed to allocate space for connectivity array for file id %d",
		  exoid);
	  ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	  err_stat = EX_FATAL;
	  goto cleanup;
	}
      elem_ctr = elem_blk_parms[parm_ndx].elem_ctr;
    }
    /*  For each side in side set, use the appropriate lookup table to  
	determine the nodes from the connect array. */

    elem_num = elem-1;/* element number 0-based*/
    /* calculate the relative element number position in it's block*/

    elem_num_pos = elem_num - 
      (elem_blk_parms[parm_ndx].elem_ctr -
       elem_blk_parms[parm_ndx].num_elem_in_blk);

    /* calculate the beginning of the node list for this element by
       using the ss_elem_node_ndx index into the side_sets_node_index
       and adding the element number position * number of nodes per elem */

    num_nodes_per_elem = elem_blk_parms[parm_ndx].num_nodes_per_elem;
    connect_offset = num_nodes_per_elem*elem_num_pos;
    side_num = side-1;

    if (int_size == sizeof(int64_t)) {
      node_pos = ((int64_t*)ss_elem_node_ndx)[elem_ndx];
    } else {
      node_pos = ((int*)ss_elem_node_ndx)[elem_ndx];
    }

    switch (elem_blk_parms[parm_ndx].elem_type_val)
      {
      case EX_EL_CIRCLE:
      case EX_EL_SPHERE:
	{ /* Note: no side-node lookup table is used for this simple case */
	  get_nodes(exoid, side_set_node_list, node_pos, connect, connect_offset);
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, 1);   /* 1 node object */
	  break;
	}
      case EX_EL_TRUSS:
      case EX_EL_BEAM:
	{ /* Note: no side-node lookup table is used for this simple case */
	  for (i=0; i < num_nodes_per_elem; i++) {
	    get_nodes(exoid, side_set_node_list, node_pos+i, connect, connect_offset+i);
	  }
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, num_nodes_per_elem);
	  break;
	}
      case EX_EL_TRIANGLE:
	{
	  if (side_num+1 < 1 || side_num+1 > 5) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid triangle edge number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  if (ndim == 2)   /* 2d TRIs */
	    {
	      get_nodes(exoid, side_set_node_list, node_pos,   connect, connect_offset+tri_table[side_num][0]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+tri_table[side_num][1]-1);
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 2);   /* 2 node object */
	      if (num_nodes_per_elem > 3)   /* 6-node TRI  */
		{
		  get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+tri_table[side_num][2]-1);
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 3); /* 3 node object */
		}
	    }
	  else if (ndim == 3)  /* 3d TRIs */
	    {
	      get_nodes(exoid, side_set_node_list, node_pos,   connect, connect_offset+tri3_table[side_num][0]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+tri3_table[side_num][1]-1);
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 2);   /* 2 node object */
	      if (side_num+1 <= 2)  /* 3- or 6-node face */
		{
		  if (num_nodes_per_elem == 3)  /* 3-node face */
		    {
		      set_count(exoid, side_set_node_cnt_list, elem_ndx, 3); /* 3 node object */
		      get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+tri3_table[side_num][2]-1);
		    }
		  else   /* 6-node face */
		    {
		      set_count(exoid, side_set_node_cnt_list, elem_ndx, 6); /* 6 node object */
		      get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+tri3_table[side_num][2]-1);
		      get_nodes(exoid, side_set_node_list, node_pos+3, connect, connect_offset+tri3_table[side_num][3]-1);
		      get_nodes(exoid, side_set_node_list, node_pos+4, connect, connect_offset+tri3_table[side_num][4]-1);
		      get_nodes(exoid, side_set_node_list, node_pos+5, connect, connect_offset+tri3_table[side_num][5]-1);
		    }
		}
	      else /* 2- or 3-node edge */
		{
		  if (num_nodes_per_elem > 3)  /* 3-node edge */
		    {
		      set_count(exoid, side_set_node_cnt_list, elem_ndx, 3); /* 3 node object */
		      get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+tri3_table[side_num][2]-1);
		    }
		}
	    }
	  break;
	}
      case EX_EL_QUAD:
	{
	  if (side_num+1 < 1 || side_num+1 > 4) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid quad edge number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }
        
	  get_nodes(exoid, side_set_node_list, node_pos+0, connect, connect_offset+quad_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+quad_table[side_num][1]-1);
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, 2);   /* 2 node object */
	  if (num_nodes_per_elem > 5)
	    {
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 3); /* 3 node object */
	      get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+quad_table[side_num][2]-1);
	    }
	  break;
	}
      case EX_EL_SHELL:
	{
	  if (side_num+1 < 1 || side_num+1 > 6) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid shell face number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  get_nodes(exoid, side_set_node_list, node_pos+0, connect, connect_offset+shell_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+shell_table[side_num][1]-1);
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, 2);   /* 2 node object */
	  if (num_nodes_per_elem > 2) /*** KLUDGE for 2D shells ***/
	    {
	      if (side_num+1 <= 2)  /* 4-node face */
		{
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 4);   /* 4 node object */
		  get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+shell_table[side_num][2]-1);
		  get_nodes(exoid, side_set_node_list, node_pos+3, connect, connect_offset+shell_table[side_num][3]-1);
		}
	    }
	  if (num_nodes_per_elem == 8)
	    {
	      if (side_num+1 <= 2)  /* 8-node face */
		{
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 8); /* 8 node object */
		  get_nodes(exoid, side_set_node_list, node_pos+4, connect, connect_offset+shell_table[side_num][4]-1);
		  get_nodes(exoid, side_set_node_list, node_pos+5, connect, connect_offset+shell_table[side_num][5]-1);
		  get_nodes(exoid, side_set_node_list, node_pos+6, connect, connect_offset+shell_table[side_num][6]-1);
		  get_nodes(exoid, side_set_node_list, node_pos+7, connect, connect_offset+shell_table[side_num][7]-1);
		}
	      else 
		{
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 3); /* 3 node edge */
		  get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+shell_table[side_num][2]-1);
		}
	    }
	  break;
	}
      case EX_EL_TETRA:
	{
	  if (side_num+1 < 1 || side_num+1 > 4) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid tetra face number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  get_nodes(exoid, side_set_node_list, node_pos+0, connect, connect_offset+tetra_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+tetra_table[side_num][1]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+tetra_table[side_num][2]-1);
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, 3);   /* 3 node object */
	  if (num_nodes_per_elem == 8)
	    {
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 4); /* 4 node object */
	      get_nodes(exoid, side_set_node_list, node_pos+3, connect, connect_offset+tetra_table[side_num][3]-1);
	    }
	  else if (num_nodes_per_elem > 8)
	    {
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 6); /* 6 node object */
	      get_nodes(exoid, side_set_node_list, node_pos+3, connect, connect_offset+tetra_table[side_num][3]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+4, connect, connect_offset+tetra_table[side_num][4]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+5, connect, connect_offset+tetra_table[side_num][5]-1);
	    }
	  break;
	}
      case EX_EL_WEDGE:
	{
	  if (side_num+1 < 1 || side_num+1 > 5) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid wedge face number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][1]-1);
	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][2]-1);

	  if (wedge_table[side_num][3] == 0) { /* degenerate side? */
	    set_count(exoid, side_set_node_cnt_list, elem_ndx, 3);   /* 3 node side */
	  }
	  else
	    {
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][3]-1);
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 4);   /* 4 node side */
	    }


	  if (num_nodes_per_elem > 6)
	    {
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][4]-1);
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][5]-1);
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][6]-1);

	      if (wedge_table[side_num][7] == 0) /* degenerate side? */
		set_count(exoid, side_set_node_cnt_list, elem_ndx, 7);   /* 6 node side */
	      else
		{
		  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+wedge_table[side_num][7]-1);
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 8);   /* 8 node side */
		}
	    }
	  break;
	}
      case EX_EL_PYRAMID:
	{
	  if (side_num+1 < 1 || side_num+1 > 5) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid pyramid face number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][1]-1);
	  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][2]-1);

	  if (pyramid_table[side_num][3] == 0) { /* degenerate side? */
	    set_count(exoid, side_set_node_cnt_list, elem_ndx, 3);   /* 3 node side */
	  }
	  else
	    {
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][3]-1);
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 4);   /* 4 node side */
	    }


	  if (num_nodes_per_elem > 5)
	    {
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][4]-1);
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][5]-1);
	      get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][6]-1);

	      if (pyramid_table[side_num][7] == 0) /* degenerate side? */
		set_count(exoid, side_set_node_cnt_list, elem_ndx, 7);   /* 6 node side */
	      else
		{
		  get_nodes(exoid, side_set_node_list, node_pos++, connect, connect_offset+pyramid_table[side_num][7]-1);
		  set_count(exoid, side_set_node_cnt_list, elem_ndx, 8);   /* 8 node side */
		}
	    }
	  break;
	}
      case EX_EL_HEX:
	{
	  if (side_num+1 < 1 || side_num+1 > 6) /* side number range check */
	    {
	      exerrval = EX_BADPARAM;
	      sprintf(errmsg,
		      "Error: Invalid hex face number %"ST_ZU" in file id %d",
		      side_num+1, exoid);
	      ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	      err_stat = EX_FATAL;
	      goto cleanup;
	    }

	  get_nodes(exoid, side_set_node_list, node_pos+0, connect, connect_offset+hex_table[side_num][0]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+1, connect, connect_offset+hex_table[side_num][1]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+2, connect, connect_offset+hex_table[side_num][2]-1);
	  get_nodes(exoid, side_set_node_list, node_pos+3, connect, connect_offset+hex_table[side_num][3]-1);
	  set_count(exoid, side_set_node_cnt_list, elem_ndx, 4);   /* 4 node object */
	  if (num_nodes_per_elem > 12)   /* more nodes than HEXSHELL */
	    {
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 8); /* 8 node object */
	      get_nodes(exoid, side_set_node_list, node_pos+4, connect, connect_offset+hex_table[side_num][4]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+5, connect, connect_offset+hex_table[side_num][5]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+6, connect, connect_offset+hex_table[side_num][6]-1);
	      get_nodes(exoid, side_set_node_list, node_pos+7, connect, connect_offset+hex_table[side_num][7]-1);
	    }
	  if (num_nodes_per_elem == 27)                 /* 27-node brick */
	    {
	      set_count(exoid, side_set_node_cnt_list, elem_ndx, 9); /* 9 node object */
	      get_nodes(exoid, side_set_node_list, node_pos+8, connect, connect_offset+hex_table[side_num][8]-1);
	    }
	  break;
	}
      default:
	{
	  exerrval = EX_BADPARAM;
	  sprintf(errmsg,
		  "Error: %s is an unsupported element type",
		  elem_blk_parms[parm_ndx].elem_type);
	  ex_err("ex_get_side_set_node_list",errmsg,exerrval);
	  return(EX_FATAL);
	}
      }
  }


  /* All done: release connectivity array space, element block ids array,
     element block parameters array, and side set element index array */
 cleanup:
  safe_free(connect);
  safe_free(ss_parm_ndx);
  safe_free(elem_blk_ids);
  safe_free(elem_blk_parms);
  safe_free(ss_elem_ndx);
  safe_free(ss_elem_node_ndx);
  safe_free(side_set_side_list);
  safe_free(side_set_elem_list);

  return(err_stat);
}
