/* This file is part of the Neper software package. */
/* Copyright (C) 2003-2014, Romain Quey. */
/* See the COPYING file in the top-level directory. */

#include"neut_mesh_op_.h"

struct MESH *
neut_mesh_alloc_1d (int size)
{
  struct MESH *array = (struct MESH *) calloc (size, sizeof (struct MESH));

  if (!array)
    ut_print_message (2, 0, "memory allocation failed! (%d)\n", size);

  return array;
}

void
neut_mesh_free_1d (struct MESH *array, int size)
{
  int i;

  if (!array)
    return;

  for (i = 0; i < size; i++)
    neut_mesh_free (array + i);

  free (array);
  array = NULL;

  return;
}


struct MESH
neut_mesh_alloc (int Dimension, char *EltType, int EltOrder, int EltQty,
		 int ElsetQty)
{
  struct MESH Mesh;
  int eltnodeqty = neut_elt_nodeqty (EltType, Dimension, EltOrder);

  neut_mesh_set_zero (&Mesh);

  Mesh.Dimension = Dimension;
  Mesh.EltOrder = EltOrder;
  ut_string_string (EltType, &(Mesh.EltType));

  Mesh.EltQty = EltQty;

  if (EltQty > 0)
  {
    Mesh.EltNodes = ut_alloc_2d_int (EltQty + 1, eltnodeqty);
    Mesh.EltElset = ut_alloc_1d_int (EltQty + 1);
  }
  else
  {
    Mesh.EltNodes = NULL;
    Mesh.EltElset = NULL;
  }

  Mesh.ElsetQty = ElsetQty;
  Mesh.ElsetId  = NULL;

  if (ElsetQty > 0)
    Mesh.Elsets = ut_alloc_2d_int (Mesh.ElsetQty + 1, 2);
  else
    Mesh.Elsets = NULL;

  Mesh.NodeQty = 0;
  Mesh.NodeElts = NULL;

  return Mesh;
}

void
neut_mesh_free (struct MESH *pMesh)
{
  if (pMesh == NULL)
    return;

  ut_free_1d_char ((*pMesh).EltType);

  ut_free_2d_int ((*pMesh).EltNodes, (*pMesh).EltQty + 1);

  ut_free_1d_int ((*pMesh).ElsetId);

  ut_free_2d_int ((*pMesh).Elsets, (*pMesh).ElsetQty + 1);

  ut_free_1d_int ((*pMesh).EltElset);

  if ((*pMesh).NodeElts != NULL)
    ut_free_2d_int ((*pMesh).NodeElts, (*pMesh).NodeQty + 1);

  neut_mesh_set_zero (pMesh);

  return;
}

void
neut_mesh_set_zero (struct MESH *pMesh)
{
  (*pMesh).Dimension = 0;
  (*pMesh).EltOrder = 0;
  (*pMesh).EltType = NULL;

  (*pMesh).EltQty = 0;
  (*pMesh).ElsetQty = 0;

  (*pMesh).EltNodes = NULL;

  (*pMesh).ElsetId = NULL;
  (*pMesh).Elsets = NULL;

  (*pMesh).NodeQty = 0;

  (*pMesh).NodeElts = NULL;
  (*pMesh).EltElset = NULL;

  return;
}

void
neut_mesh_memcpy (struct MESH Old, struct MESH *pNew)
{
  int i, eltnodeqty;

  neut_mesh_free (pNew);

  (*pNew).Dimension = Old.Dimension;
  (*pNew).EltOrder = Old.EltOrder;
  (*pNew).EltQty = Old.EltQty;
  (*pNew).NodeQty = Old.NodeQty;
  (*pNew).ElsetQty = Old.ElsetQty;

  if (Old.EltType)
  {
    (*pNew).EltType = ut_alloc_1d_char (strlen (Old.EltType) + 1);
    strcpy ((*pNew).EltType, Old.EltType);
  }

  if (Old.EltNodes)
  {
    eltnodeqty =
      neut_elt_nodeqty ((*pNew).EltType, (*pNew).Dimension, (*pNew).EltOrder);

    (*pNew).EltNodes = ut_alloc_2d_int (Old.EltQty + 1, eltnodeqty);

    for (i = 1; i <= Old.EltQty; i++)
      ut_array_1d_int_memcpy ((*pNew).EltNodes[i], eltnodeqty,
			      Old.EltNodes[i]);
  }

  if (Old.ElsetId)
  {
    (*pNew).ElsetId = ut_alloc_1d_int (Old.ElsetQty + 1);
    ut_array_1d_int_memcpy ((*pNew).ElsetId + 1, Old.ElsetQty,
	                    Old.ElsetId + 1);
  }

  if (Old.Elsets)
  {
    (*pNew).Elsets = ut_alloc_1d_pint (Old.ElsetQty + 1);
    for (i = 1; i <= Old.ElsetQty; i++)
    {
      (*pNew).Elsets[i] = ut_alloc_1d_int (Old.Elsets[i][0] + 1);
      ut_array_1d_int_memcpy ((*pNew).Elsets[i], Old.Elsets[i][0] + 1,
			      Old.Elsets[i]);
    }
  }

  if (Old.EltElset)
  {
    (*pNew).EltElset = ut_alloc_1d_int (Old.EltQty + 1);
    ut_array_1d_int_memcpy ((*pNew).EltElset + 1, Old.EltQty,
			    Old.EltElset + 1);
  }

  return;
}

#ifdef HAVE_LIBSCOTCH
void
neut_mesh_scotchmesh (struct MESH Mesh, int vnodnbr, SCOTCH_Mesh * pSCMesh)
{
  int i, id, status;
  int velmbas, vnodbas, velmnbr, vertnbr, edgenbr;
  int *verttab, *edgetab;
  int eltnodeqty =
    neut_elt_nodeqty (Mesh.EltType, Mesh.Dimension, Mesh.EltOrder);

  velmbas = 1;			/* element base number */
  vnodbas = 1 + Mesh.EltQty;	/* node base number */
  velmnbr = Mesh.EltQty;

  vertnbr = velmnbr + vnodnbr;
  edgenbr = 2 * velmnbr * eltnodeqty;

  verttab = ut_alloc_1d_int (vertnbr + 1);
  edgetab = ut_alloc_1d_int (edgenbr + 1);

  /* recording elements */
  id = 0;
  for (i = 1; i <= velmnbr; i++)
  {
    verttab[i - 1] = id + 1;
    ut_array_1d_int_memcpy (edgetab + id, eltnodeqty, Mesh.EltNodes[i]);
    ut_array_1d_int_addval (edgetab + id, eltnodeqty, vnodbas - 1,
			    edgetab + id);
    id += eltnodeqty;
  }

  /* recording nodes */
  for (i = 1; i <= vnodnbr; i++)
  {
    verttab[i + Mesh.EltQty - 1] = id + 1;
    ut_array_1d_int_memcpy (edgetab + id, Mesh.NodeElts[i][0],
			    Mesh.NodeElts[i] + 1);
    id += Mesh.NodeElts[i][0];
  }
  verttab[vertnbr] = edgenbr + 1;

  SCOTCH_meshBuild (pSCMesh, velmbas, vnodbas, velmnbr, vnodnbr, verttab,
		    NULL, NULL, NULL, NULL, edgenbr, edgetab);
  /*
     file = ut_file_open ("dd", "w");
     SCOTCH_meshSave (pSCMesh, file);
     ut_file_close (file, "dd", "w");
   */

  status = SCOTCH_meshCheck (pSCMesh);
  if (status != 0)
    printf ("error");

  return;
}
#endif

/* nodes & elements are considered to be numbered contiguously from 1 */
void
neut_mesh_init_nodeelts (struct MESH *pMesh, int NodeQty)
{
  int i, j, eltnodeqty, node;

  if (pMesh == NULL || (*pMesh).EltQty == 0)
    return;

  eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				 (*pMesh).EltOrder);

  if (NodeQty <= 0)
    for (i = 1; i <= (*pMesh).EltQty; i++)
      for (j = 0; j < eltnodeqty; j++)
	NodeQty = ut_num_max (NodeQty, (*pMesh).EltNodes[i][j]);

  (*pMesh).NodeElts = ut_alloc_2d_int (NodeQty + 1, 1);
  (*pMesh).NodeQty = NodeQty;

  /* first pass: recording, for each node, the quantity of elements */
  for (i = 1; i <= (*pMesh).EltQty; i++)
    for (j = 0; j < eltnodeqty; j++)
      (*pMesh).NodeElts[(*pMesh).EltNodes[i][j]][0]++;

  /* allocation */
  for (i = 1; i <= NodeQty; i++)
    (*pMesh).NodeElts[i] =
      ut_realloc_1d_int ((*pMesh).NodeElts[i], (*pMesh).NodeElts[i][0] + 1);

  ut_array_2d_int_zero ((*pMesh).NodeElts + 1, NodeQty, 1);

  /* recording node elements */
  for (i = 1; i <= (*pMesh).EltQty; i++)
    for (j = 0; j < eltnodeqty; j++)
    {
      node = (*pMesh).EltNodes[i][j];
      (*pMesh).NodeElts[node][++(*pMesh).NodeElts[node][0]] = i;
    }

  return;
}

void
neut_mesh_init_eltelset (struct MESH *pMesh, int *elset_nbs)
{
  int i, j;

  (*pMesh).EltElset =
    ut_realloc_1d_int ((*pMesh).EltElset, (*pMesh).EltQty + 1);

  if (elset_nbs != NULL)
    for (i = 1; i <= (*pMesh).ElsetQty; i++)
      for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
	(*pMesh).EltElset[(*pMesh).Elsets[i][j]] = elset_nbs[i];
  else
    for (i = 1; i <= (*pMesh).ElsetQty; i++)
      for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
	(*pMesh).EltElset[(*pMesh).Elsets[i][j]] = i;

  return;
}

void
neut_mesh_init_elsets (struct MESH *pMesh)
{
  int i, j, k, elt, neltqty, node, firstelt;
  int *elts = NULL;
  int *nelts = NULL;

  neut_mesh_init_nodeelts (pMesh, 0);

  (*pMesh).ElsetQty = 0;
  for (i = 1; i <= (*pMesh).EltQty; i++)
    (*pMesh).ElsetQty = ut_num_max ((*pMesh).EltElset[i], (*pMesh).ElsetQty);

  (*pMesh).Elsets = ut_alloc_2d_int ((*pMesh).ElsetQty + 1, 1);
  for (i = 1; i <= (*pMesh).EltQty; i++)
    neut_mesh_elset_addelt (pMesh, (*pMesh).EltElset[i], i);

  // In case of 1D elements, ordering elements in the elsets, as well as
  // the nodes in the elements.
  if ((*pMesh).Dimension == 1)
  {
    for (i = 1; i <= (*pMesh).ElsetQty; i++)
    {
      // nothing to do (don't remove this)
      if ((*pMesh).Elsets[i][0] <= 1)
	continue;

      elts = ut_alloc_1d_int ((*pMesh).Elsets[i][0] + 1);
      elts[0] = -1;

      firstelt = 0;
      // Looking for an element that is at the extremity of the elset
      for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
      {
	elt = (*pMesh).Elsets[i][j];
	neut_mesh_elt_elset_neighelts (*pMesh, elt, i, &nelts, &neltqty);

	if (neltqty <= 1)
	{
	  firstelt = elt;
	  break;
	}
      }

      int loopelset = 0;

      if (firstelt > 0)
	elts[1] = firstelt;

      // an exception is when the 1D mesh is reconstructed from a 2D mesh
      // with a single elset (e.g. a mesh of a face). In this case, the
      // 1D elset is a loop, which must be handled in a slightly different way.
      else
      {
	loopelset = 1;
	elts[1] = (*pMesh).Elsets[i][1];
      }

      if (loopelset == 0)
      {
	// Ordering the nodes of the first element - the first node must
	// be at the extremity of the elset
	node = (*pMesh).EltNodes[elts[1]][0];

	// == 1 means no other elset, so is at the extremity
	// == 2 means has 2 elts, that may or may not belong the the
	// current elset.  If not, the node is at the extremity.
	// Otherwise, need to reverse nodes.
	if ((*pMesh).NodeElts[node][0] == 2)
	{
	  if ((*pMesh).EltElset[(*pMesh).NodeElts[node][1]]
	   == (*pMesh).EltElset[(*pMesh).NodeElts[node][2]])
	  neut_mesh_elt_reversenodes (pMesh, elts[1]);
	}
      }

      for (j = 2; j <= (*pMesh).Elsets[i][0]; j++)
      {
	neut_mesh_elt_elset_neighelts (*pMesh, elts[j - 1], i, &nelts, &neltqty);

	// for an open elset (general case), the neighbour must belong to
	// the same elset and be different from the already registered
	// element (j - 2).
	if (loopelset == 0)
	{
	  for (k = 0; k < neltqty; k++)
	    if (nelts[k] != elts[j - 2])
	    {
	      elts[j] = nelts[k];
	      break;
	    }
	}
	// for a loop elset, the neighbour must share the second node of
	// the previous element.
	else
	{
	  for (k = 0; k < neltqty; k++)
	  {
	    if (ut_array_1d_int_eltpos ((*pMesh).EltNodes[nelts[k]], 2,
					(*pMesh).EltNodes[elts[j - 1]][1]) !=
		-1)
	    {
	      elts[j] = nelts[k];
	      break;
	    }
	  }
	}

	if (elts[j] == 0)
	  ut_error_reportbug ();

	// reversing elt nodes if necessary
	if ((*pMesh).EltNodes[elts[j]][0] !=
	    (*pMesh).EltNodes[elts[j - 1]][1])
	{
	  neut_mesh_elt_reversenodes (pMesh, elts[j]);
	  /*
	  if ((*pMesh).EltNodes[elts[j]][0]
	      != (*pMesh).EltNodes[elts[j - 1]][1])
	    ut_error_reportbug ();
	    */
	}
      }

      // recording new element sequence
      ut_array_1d_int_memcpy ((*pMesh).Elsets[i] + 1, (*pMesh).Elsets[i][0],
			      elts + 1);

      ut_free_1d_int (elts);
    }
  }

  ut_free_1d_int (nelts);

  return;
}

void
neut_mesh_order1to2 (struct NODES *pNodes, struct MESH *pMesh1D,
		     struct MESH *pMesh2D, struct MESH *pMesh3D)
{
  int i, j, k, pos1, pos2, pos, eltnb, node, qty,
      nodeqty_1d1, nodeqty_1d, nodeqty_2d1, nodeqty_2d,
      nodeqty_3d1, nodeqty_3d;
  int **fir3 = NULL, **sec3 = NULL, **fir2 = NULL, **sec2 = NULL,
      **fir1 = NULL, **sec1 = NULL;
  double *coo = ut_alloc_1d (3);
  int *eltlist = NULL;
  int *inter = NULL;
  int qty_bak = (*pNodes).NodeQty;
  int node2min, node2max;
  int node1min, node1max;
  int *nodes = ut_alloc_1d_int (2);
  int do_1d, do_2d, do_3d;

  do_1d = pMesh1D && (*pMesh1D).EltQty > 0;
  do_2d = pMesh2D && (*pMesh2D).EltQty > 0;
  do_3d = pMesh3D && (*pMesh3D).EltQty > 0;

  nodeqty_1d1 = neut_elt_nodeqty ((*pMesh1D).EltType, 1, 1);
  nodeqty_1d  = neut_elt_nodeqty ((*pMesh1D).EltType, 1, 2);
  nodeqty_2d1 = neut_elt_nodeqty ((*pMesh2D).EltType, 2, 1);
  nodeqty_2d  = neut_elt_nodeqty ((*pMesh2D).EltType, 2, 2);
  nodeqty_3d1 = neut_elt_nodeqty ((*pMesh3D).EltType, 3, 1);
  nodeqty_3d  = neut_elt_nodeqty ((*pMesh3D).EltType, 3, 2);

  if (do_3d)
  {
    fir3 = ut_alloc_2d_int (nodeqty_3d, 2);
    sec3 = ut_alloc_2d_int (nodeqty_3d1, nodeqty_3d1);

    if (!strcmp ((*pMesh3D).EltType, "tri"))
    {
      sec3[0][1] = sec3[1][0] = 4;
      sec3[1][2] = sec3[2][1] = 5;
      sec3[2][0] = sec3[0][2] = 6;
      sec3[3][0] = sec3[0][3] = 7;
      sec3[2][3] = sec3[3][2] = 8;
      sec3[3][1] = sec3[1][3] = 9;

      fir3[4][0] = 0;
      fir3[4][1] = 1;
      fir3[5][0] = 1;
      fir3[5][1] = 2;
      fir3[6][0] = 2;
      fir3[6][1] = 0;
      fir3[7][0] = 3;
      fir3[7][1] = 0;
      fir3[8][0] = 2;
      fir3[8][1] = 3;
      fir3[9][0] = 3;
      fir3[9][1] = 1;
    }
    else if (!strcmp ((*pMesh3D).EltType, "quad"))
    {
      sec3[0][1] = sec3[1][0] = 8;
      sec3[4][5] = sec3[5][4] = 16;
      sec3[3][2] = sec3[2][3] = 13;
      sec3[7][6] = sec3[6][7] = 19;

      sec3[0][4] = sec3[4][0] = 10;
      sec3[1][5] = sec3[5][1] = 12;
      sec3[3][7] = sec3[7][3] = 15;
      sec3[2][6] = sec3[6][2] = 14;

      sec3[0][3] = sec3[3][0] = 9;
      sec3[1][2] = sec3[2][1] = 11;
      sec3[4][7] = sec3[7][4] = 17;
      sec3[5][6] = sec3[6][5] = 18;

      fir3[8][0] = 0;
      fir3[8][1] = 1;
      fir3[9][0] = 0;
      fir3[9][1] = 3;
      fir3[10][0] = 0;
      fir3[10][1] = 4;
      fir3[11][0] = 1;
      fir3[11][1] = 2;
      fir3[12][0] = 1;
      fir3[12][1] = 5;
      fir3[13][0] = 2;
      fir3[13][1] = 3;
      fir3[14][0] = 2;
      fir3[14][1] = 6;
      fir3[15][0] = 3;
      fir3[15][1] = 7;
      fir3[16][0] = 4;
      fir3[16][1] = 5;
      fir3[17][0] = 4;
      fir3[17][1] = 7;
      fir3[18][0] = 5;
      fir3[18][1] = 6;
      fir3[19][0] = 6;
      fir3[19][1] = 7;
    }
    else
      abort ();
  }

  if (do_2d)
  {
    fir2 = ut_alloc_2d_int (nodeqty_2d, 2);
    sec2 = ut_alloc_2d_int (nodeqty_2d1, nodeqty_2d1);

    if (!strcmp ((*pMesh2D).EltType, "tri"))
    {
      sec2[0][1] = sec2[1][0] = 3;
      sec2[1][2] = sec2[2][1] = 4;
      sec2[2][0] = sec2[0][2] = 5;

      fir2[3][0] = 0;
      fir2[3][1] = 1;
      fir2[4][0] = 1;
      fir2[4][1] = 2;
      fir2[5][0] = 2;
      fir2[5][1] = 0;
    }
    else if (!strcmp ((*pMesh2D).EltType, "quad"))
    {
      sec2[0][1] = sec2[1][0] = 4;
      sec2[1][2] = sec2[2][1] = 5;
      sec2[2][3] = sec2[3][2] = 6;
      sec2[3][0] = sec2[0][3] = 7;

      fir2[4][0] = 0;
      fir2[4][1] = 1;
      fir2[5][0] = 1;
      fir2[5][1] = 2;
      fir2[6][0] = 2;
      fir2[6][1] = 3;
      fir2[7][0] = 3;
      fir2[7][1] = 0;
    }
    else
      abort ();
  }

  fir1 = ut_alloc_2d_int (nodeqty_1d, 2);
  sec1 = ut_alloc_2d_int (nodeqty_1d1, nodeqty_1d1);

  sec1[0][1] = sec1[1][0] = 2;

  fir1[2][0] = 0;
  fir1[2][1] = 1;

/*----------------------------------------------------------------------
 * numbering:
 * nodes 0 1 2 3 at the vertices
 * 4: 0 - 1
 * 5: 1 - 2
 * 6: 2 - 0
 * 7: 3 - 0
 * 8: 2 - 3
 * 9: 3 - 1
 *
 * algorithm:
 * loop over the elements; creating the second order nodes, and, for
 * each, copying it to the other elements that share the element edge.
 * */
  /* switching to order 2 and initializing temporary variables */
  if (pMesh3D != NULL && (*pMesh3D).EltQty > 0)
    (*pMesh3D).EltOrder = 2;

  node2min = INT_MIN;
  node2max = INT_MAX;
  if (do_2d)
  {
    neut_mesh_nodemin (*pMesh2D, &node2min);
    neut_mesh_nodemax (*pMesh2D, &node2max);
    (*pMesh2D).EltOrder = 2;
  }

  node1min = INT_MIN;
  node1max = INT_MAX;
  if (do_1d)
  {
    neut_mesh_nodemin (*pMesh1D, &node1min);
    neut_mesh_nodemax (*pMesh1D, &node1max);
    (*pMesh1D).EltOrder = 2;
  }

  /* (re)allocating EltNodes  */
  if (do_3d)
  {
    ut_realloc_2d_int_size2 ((*pMesh3D).EltNodes + 1, (*pMesh3D).EltQty,
			     nodeqty_3d);

    for (i = 1; i <= (*pMesh3D).EltQty; i++)
      ut_array_1d_int_zero ((*pMesh3D).EltNodes[i] + nodeqty_3d1,
	                    nodeqty_3d - nodeqty_3d1);
  }

  if (do_2d)
  {
    ut_realloc_2d_int_size2 ((*pMesh2D).EltNodes + 1, (*pMesh2D).EltQty,
			     nodeqty_2d);

    for (i = 1; i <= (*pMesh2D).EltQty; i++)
      ut_array_1d_int_zero ((*pMesh2D).EltNodes[i] + nodeqty_2d1,
	                    nodeqty_2d - nodeqty_2d1);
  }

  if (do_1d)
  {
    ut_realloc_2d_int_size2 ((*pMesh1D).EltNodes + 1, (*pMesh1D).EltQty,
	                     nodeqty_1d);

    for (i = 1; i <= (*pMesh1D).EltQty; i++)
      ut_array_1d_int_zero ((*pMesh1D).EltNodes[i] + nodeqty_1d1,
	                   nodeqty_1d - nodeqty_1d1);
  }

  // If the 3D mesh is defined -----------------------------------------

  /* for every element */
  if (do_3d)
    for (i = 1; i <= (*pMesh3D).EltQty; i++)
    {
      /* for every second order node */
      for (j = nodeqty_3d1; j < nodeqty_3d; j++)
	/* if not recorded yet */
	if ((*pMesh3D).EltNodes[i][j] == 0)
	{
	  /* recording corresponding first order nodes */
	  nodes[0] = (*pMesh3D).EltNodes[i][fir3[j][0]];
	  nodes[1] = (*pMesh3D).EltNodes[i][fir3[j][1]];

	  /* searching com elements between the two */
	  neut_mesh_nodes_comelts (*pMesh3D, nodes, 2, &eltlist, &qty);

	  /* if the first element is >= i, that means that the node at
	   * this position has not been recorded through another elements
	   * before; recording it in 3D mesh, so as lower dimension meshes */
	  if (eltlist[0] < i)
	    ut_error_reportbug ();

	  for (k = 0; k < 3; k++)
	    coo[k] = .5 * ((*pNodes).NodeCoo[nodes[0]][k]
			   + (*pNodes).NodeCoo[nodes[1]][k]);

	  neut_nodes_addnode (pNodes, coo, -1);

	  node = (*pNodes).NodeQty;

	  /* adding node to 3D mesh */
	  (*pMesh3D).EltNodes[i][j] = node;

	  for (k = 1; k < qty; k++)
	  {
	    eltnb = eltlist[k];
	    pos1 =
	      ut_array_1d_int_eltpos ((*pMesh3D).EltNodes[eltnb], nodeqty_3d1,
				      nodes[0]);
	    pos2 =
	      ut_array_1d_int_eltpos ((*pMesh3D).EltNodes[eltnb], nodeqty_3d1,
				      nodes[1]);
	    pos = sec3[pos1][pos2];
	    (*pMesh3D).EltNodes[eltnb][pos] = node;
	  }

	  /* adding node to 2D mesh if needed */
	  if (do_2d)
	    if (nodes[0] >= node2min && nodes[0] <= node2max &&
		nodes[1] >= node2min && nodes[1] <= node2max)
	    {
	      /* searching com elts between the two */
	      neut_mesh_nodes_comelts (*pMesh2D, nodes, 2, &eltlist, &qty);

	      /* note: qty may be zero */
	      for (k = 0; k < qty; k++)
	      {
		eltnb = eltlist[k];
		if (ut_array_1d_int_eltpos ((*pMesh2D).EltNodes[eltnb],
					    nodeqty_2d, node) == -1)
		{
		  pos1 =
		    ut_array_1d_int_eltpos ((*pMesh2D).EltNodes[eltnb],
					    nodeqty_2d1, nodes[0]);
		  pos2 =
		    ut_array_1d_int_eltpos ((*pMesh2D).EltNodes[eltnb],
					    nodeqty_2d1, nodes[1]);
		  pos = sec2[pos1][pos2];
		  (*pMesh2D).EltNodes[eltnb][pos] = node;
		}
	      }
	    }

	  /* adding node to 1D mesh if needed */
	  if (do_1d)
	    if (nodes[0] >= node1min && nodes[0] <= node1max &&
		nodes[1] >= node1min && nodes[1] <= node1max)
	    {
	      /* searching com elts between the two */
	      neut_mesh_nodes_comelts (*pMesh1D, nodes, 2, &eltlist, &qty);

	      for (k = 0; k < qty; k++)
	      {
		eltnb = eltlist[k];
		if (ut_array_1d_int_eltpos ((*pMesh1D).EltNodes[eltnb],
					    nodeqty_1d, node) == -1)
		  (*pMesh1D).EltNodes[eltnb][2] = node;
	      }
	    }
	}
    }

  // Else if the 2D mesh is defined ------------------------------------

  /* for every element */
  if (do_2d)
    for (i = 1; i <= (*pMesh2D).EltQty; i++)
    {
      /* for every second order node */
      for (j = nodeqty_2d1; j < nodeqty_2d; j++)
	/* if not recorded yet */
	if ((*pMesh2D).EltNodes[i][j] == 0)
	{
	  /* recording corresponding first order nodes */
	  nodes[0] = (*pMesh2D).EltNodes[i][fir2[j][0]];
	  nodes[1] = (*pMesh2D).EltNodes[i][fir2[j][1]];

	  /* searching com elements between the two */
	  neut_mesh_nodes_comelts (*pMesh2D, nodes, 2, &eltlist, &qty);

	  /* if the first element is >= i, that means that the node at
	   * this position has not been recorded through another elements
	   * before; recording it in 2D mesh, so as lower dimension meshes */
	  if (eltlist[0] < i)
	    ut_error_reportbug ();

	  for (k = 0; k < 3; k++)
	    coo[k] = .5 * ((*pNodes).NodeCoo[nodes[0]][k]
			   + (*pNodes).NodeCoo[nodes[1]][k]);

	  neut_nodes_addnode (pNodes, coo, -1);

	  node = (*pNodes).NodeQty;

	  /* adding node to 2D mesh */
	  (*pMesh2D).EltNodes[i][j] = node;

	  for (k = 1; k < qty; k++)
	  {
	    eltnb = eltlist[k];
	    pos1 =
	      ut_array_1d_int_eltpos ((*pMesh2D).EltNodes[eltnb],
				      nodeqty_2d1, nodes[0]);
	    pos2 =
	      ut_array_1d_int_eltpos ((*pMesh2D).EltNodes[eltnb],
		                      nodeqty_2d1, nodes[1]);
	    pos = sec2[pos1][pos2];
	    (*pMesh2D).EltNodes[eltnb][pos] = node;
	  }

	  /* adding node to 1D mesh if needed */
	  if (do_1d)
	    if (nodes[0] >= node1min && nodes[0] <= node1max &&
		nodes[1] >= node1min && nodes[1] <= node1max)
	    {
	      /* searching com elts between the two */
	      neut_mesh_nodes_comelts (*pMesh1D, nodes, 2, &eltlist, &qty);

	      for (k = 0; k < qty; k++)
	      {
		eltnb = eltlist[k];
		if (ut_array_1d_int_eltpos ((*pMesh1D).EltNodes[eltnb],
					    nodeqty_1d, node) == -1)
		  (*pMesh1D).EltNodes[eltnb][2] = node;
	      }
	    }
	}
    }

  /* for every element */
  if (do_1d)
    for (i = 1; i <= (*pMesh1D).EltQty; i++)
    {
      /* for every second order node */
      for (j = nodeqty_1d1; j < nodeqty_1d; j++)
	/* if not recorded yet */
	if ((*pMesh1D).EltNodes[i][j] == 0)
	{
	  /* recording corresponding first order nodes */
	  nodes[0] = (*pMesh1D).EltNodes[i][fir1[j][0]];
	  nodes[1] = (*pMesh1D).EltNodes[i][fir1[j][1]];

	  /* searching com elements between the two */
	  neut_mesh_nodes_comelts (*pMesh1D, nodes, 2, &eltlist, &qty);

	  /* if the first element is >= i, that means that the node at
	   * this position has not been recorded through another elements
	   * before; recording it in 1D mesh, so as lower dimension meshes */
	  if (eltlist[0] < i)
	    ut_error_reportbug ();

	  for (k = 0; k < 3; k++)
	    coo[k] = .5 * ((*pNodes).NodeCoo[nodes[0]][k]
			   + (*pNodes).NodeCoo[nodes[1]][k]);

	  neut_nodes_addnode (pNodes, coo, -1);

	  node = (*pNodes).NodeQty;

	  /* adding node to 1D mesh */
	  (*pMesh1D).EltNodes[i][j] = node;

	  for (k = 1; k < qty; k++)
	  {
	    eltnb = eltlist[k];
	    pos1 =
	      ut_array_1d_int_eltpos ((*pMesh1D).EltNodes[eltnb], nodeqty_1d1,
				      nodes[0]);
	    pos2 =
	      ut_array_1d_int_eltpos ((*pMesh1D).EltNodes[eltnb], nodeqty_1d1,
				      nodes[1]);
	    pos = sec1[pos1][pos2];
	    (*pMesh1D).EltNodes[eltnb][pos] = node;
	  }

	  /* adding node to 1D mesh if needed */
	  if (pMesh1D != NULL)
	    if (nodes[0] >= node1min && nodes[0] <= node1max &&
		nodes[1] >= node1min && nodes[1] <= node1max)
	    {
	      /* searching com elts between the two */
	      neut_mesh_nodes_comelts (*pMesh1D, nodes, 2, &eltlist, &qty);

	      for (k = 0; k < qty; k++)
	      {
		eltnb = eltlist[k];
		if (ut_array_1d_int_eltpos ((*pMesh1D).EltNodes[eltnb],
					    nodeqty_1d, node) == -1)
		  (*pMesh1D).EltNodes[eltnb][2] = node;
	      }
	    }
	}
    }

  // Testing meshes
  if (do_3d && ut_array_2d_int_eltpos ((*pMesh3D).EltNodes + 1,
					 (*pMesh3D).EltQty, nodeqty_3d, 0, NULL,
					 NULL) != -1)
    ut_error_reportbug ();

  if (do_2d && ut_array_2d_int_eltpos ((*pMesh2D).EltNodes + 1,
					 (*pMesh2D).EltQty, nodeqty_2d,
					 0, NULL, NULL) != -1)
    ut_error_reportbug ();

  if (do_1d && ut_array_2d_int_eltpos ((*pMesh1D).EltNodes + 1,
					 (*pMesh1D).EltQty, nodeqty_1d,
					 0, NULL, NULL) != -1)
    ut_error_reportbug ();


  // reinitializing 3D NodeElts
  if (do_3d)
  {
    if ((*pMesh3D).NodeElts != NULL)
      ut_free_2d_int ((*pMesh3D).NodeElts, qty_bak + 1);
    neut_mesh_init_nodeelts (pMesh3D, (*pNodes).NodeQty);
  }

  // reinitializing 2D NodeElts
  if (do_2d)
  {
    if ((*pMesh2D).NodeElts != NULL)
      ut_free_2d_int ((*pMesh2D).NodeElts, node2max + 1);
    neut_mesh_init_nodeelts (pMesh2D, (*pNodes).NodeQty);
  }

  // reinitializing 1D NodeElts
  if (do_1d)
  {
    if ((*pMesh1D).NodeElts != NULL)
      ut_free_2d_int ((*pMesh1D).NodeElts, node1max + 1);
    neut_mesh_init_nodeelts (pMesh1D, (*pNodes).NodeQty);
  }

  if (do_3d)
  {
    ut_free_2d_int (fir3, nodeqty_3d);
    ut_free_2d_int (sec3, nodeqty_3d1);
  }

  if (do_2d)
  {
    ut_free_2d_int (fir2, nodeqty_2d);
    ut_free_2d_int (sec2, nodeqty_2d1);
  }

  if (do_1d)
  {
    ut_free_2d_int (fir1, nodeqty_1d);
    ut_free_2d_int (sec1, nodeqty_1d1);
  }

  ut_free_1d (coo);
  ut_free_1d_int (inter);
  ut_free_1d_int (eltlist);
  ut_free_1d_int (nodes);

  return;
}

void
neut_mesh_mergeelsets (struct MESH *pEMesh)
{
  int i, tot;

  tot = 0;
  for (i = 1; i <= (*pEMesh).ElsetQty; i++)
    tot += (*pEMesh).Elsets[i][0];

  (*pEMesh).Elsets[1] = ut_realloc_1d_int ((*pEMesh).Elsets[1], tot + 1);

  for (i = 2; i <= (*pEMesh).ElsetQty; i++)
  {
    ut_array_1d_int_memcpy ((*pEMesh).Elsets[1] + (*pEMesh).Elsets[1][0] + 1,
			    (*pEMesh).Elsets[i][0], (*pEMesh).Elsets[i] + 1);
    (*pEMesh).Elsets[1][0] += (*pEMesh).Elsets[i][0];
    ut_free_1d_int ((*pEMesh).Elsets[i]);
  }

  if ((*pEMesh).Elsets[1][0] != tot)
    abort ();

  ut_array_1d_int_sort ((*pEMesh).Elsets[1] + 1, (*pEMesh).Elsets[1][0]);

  // FIXME
  if ((*pEMesh).ElsetId)
    ut_error_reportbug ();

  (*pEMesh).ElsetQty = 1;

  return;
}

/* returns 1 if meshes match, 0 else */
// TODO: this is brute force method; get less computer intensive
int
neut_mesh_cmp (struct NODES N1, struct MESH M1, struct NODES N2,
	       struct MESH M2)
{
  int i, j, k, l;
  int res;
  int eltnodeqty = neut_elt_nodeqty (M1.EltType, M1.Dimension, M1.EltOrder);
  double *dist = ut_alloc_1d (eltnodeqty);
  double min;

  res = 0;

  if (M1.EltQty != M2.EltQty)
    res = 0;
  else
    // loop over M1 elts; for each, loop over M2 to search a matching elt
    for (i = 1; i <= M1.EltQty; i++)
    {
      res = 0;
      for (j = 1; j <= M2.EltQty; j++)
      {
	res = 1;
	for (k = 0; k < eltnodeqty; k++)	// loop over M1 elt nodes
	{
	  for (l = 0; l < eltnodeqty; l++)	// loop over M2 elt nodes
	    dist[l] = ut_space_dist (N1.NodeCoo[M1.EltNodes[i][k]],
				     N2.NodeCoo[M2.EltNodes[j][l]]);

	  min = ut_array_1d_min (dist, eltnodeqty);
	  if (min > 1e-9)
	  {
	    res = 0;
	    break;
	  }
	}

	if (res == 1)
	  break;
      }

      if (res == 0)
	break;
    }

  ut_free_1d (dist);

  return res;
}

// Turn a quad mesh into a tri mesh by  element subdivision
void
neut_mesh_quad_tri (struct NODES Nodes, struct MESH Mesh, struct MESH *pTMesh)
{
  int i, j, k, elt;
  int **nodepos = NULL;
  int eltnodeqty = -1;
  int triqty = -1;
  // `triqty' is the number of tet/tri elements for an hex/quad element.

  if (strcmp (Mesh.EltType, "quad") != 0)
    ut_error_reportbug ();

  // Vertices of the tets describing the cube elt
  if (Mesh.Dimension == 3)
  {
    triqty = 6;
    eltnodeqty = 4;
    nodepos = ut_alloc_2d_int (triqty, eltnodeqty);

    nodepos[0][0] = 0;
    nodepos[0][1] = 1;
    nodepos[0][2] = 2;
    nodepos[0][3] = 6;

    nodepos[1][0] = 0;
    nodepos[1][1] = 3;
    nodepos[1][2] = 2;
    nodepos[1][3] = 6;

    nodepos[2][0] = 0;
    nodepos[2][1] = 6;
    nodepos[2][2] = 3;
    nodepos[2][3] = 7;

    nodepos[3][0] = 0;
    nodepos[3][1] = 6;
    nodepos[3][2] = 4;
    nodepos[3][3] = 7;

    nodepos[4][0] = 0;
    nodepos[4][1] = 4;
    nodepos[4][2] = 5;
    nodepos[4][3] = 6;

    nodepos[5][0] = 0;
    nodepos[5][1] = 5;
    nodepos[5][2] = 1;
    nodepos[5][3] = 6;
  }
  else if (Mesh.Dimension == 2)
  {
    triqty = 2;
    eltnodeqty = 3;
    nodepos = ut_alloc_2d_int (triqty, eltnodeqty);

    nodepos[0][0] = 0;
    nodepos[0][1] = 1;
    nodepos[0][2] = 2;

    nodepos[1][0] = 0;
    nodepos[1][1] = 2;
    nodepos[1][2] = 3;
  }
  else
    ut_error_reportbug ();

  // General data
  (*pTMesh).EltType = ut_alloc_1d_char (4);
  strcpy ((*pTMesh).EltType, "tri");
  (*pTMesh).Dimension = Mesh.Dimension;
  (*pTMesh).EltOrder = 1;

  (*pTMesh).EltQty = 0;
  (*pTMesh).EltNodes = ut_alloc_2d_int (Mesh.EltQty * triqty + 1, eltnodeqty);

  // Elt data
  if (Mesh.EltElset != NULL)
    (*pTMesh).EltElset = ut_alloc_1d_int (Mesh.EltQty * triqty + 1);

  for (i = 1; i <= Mesh.EltQty; i++)
    for (j = 0; j < triqty; j++)
    {
      elt = ++((*pTMesh).EltQty);

      for (k = 0; k < eltnodeqty; k++)
	(*pTMesh).EltNodes[elt][k] = Mesh.EltNodes[i][nodepos[j][k]];

      if (Mesh.EltElset != NULL)
	(*pTMesh).EltElset[elt] = Mesh.EltElset[i];
    }

  neut_mesh_init_elsets (pTMesh);

  neut_mesh_init_nodeelts (pTMesh, Nodes.NodeQty);

  ut_free_2d_int (nodepos, triqty);

  return;
}


// Turn a quad mesh into a tri mesh by  element subdivision
void
neut_mesh_quad_tri_insertnode (struct NODES* pNodes, struct MESH Mesh, struct MESH *pTMesh)
{
  int i, j, k, elt, newnode, eltnode;
  double* coo = ut_alloc_1d (3);

  if (strcmp (Mesh.EltType, "quad") != 0)
    ut_error_reportbug ();

  // General data
  (*pTMesh).EltType = ut_alloc_1d_char (4);
  strcpy ((*pTMesh).EltType, "tri");
  (*pTMesh).Dimension = Mesh.Dimension;
  (*pTMesh).EltOrder = 1;

  (*pTMesh).EltQty = 0;
  (*pTMesh).EltNodes = ut_alloc_2d_int (Mesh.EltQty * 4 + 1, 3);

  // Elt data
  if (Mesh.EltElset)
    (*pTMesh).EltElset = ut_alloc_1d_int (Mesh.EltQty * 4 + 1);

  for (i = 1; i <= Mesh.EltQty; i++)
  {
    newnode = (*pNodes).NodeQty + 1;
    neut_mesh_elt_centre (*pNodes, Mesh, i, coo);
    neut_nodes_addnode (pNodes, coo, 0);

    for (j = 0; j < 4; j++)
    {
      elt = ++((*pTMesh).EltQty);

      (*pTMesh).EltNodes[elt][0] = newnode;
      for (k = 1; k < 3; k++)
      {
	eltnode = Mesh.EltNodes[i][ut_num_rotpos (0, 3, j, k - 1)];
	(*pTMesh).EltNodes[elt][k] = eltnode;
      }

      if (Mesh.EltElset)
	(*pTMesh).EltElset[elt] = Mesh.EltElset[i];
    }
  }

  neut_mesh_init_elsets (pTMesh);

  neut_mesh_init_nodeelts (pTMesh, (*pNodes).NodeQty);

  ut_free_1d (coo);

  return;
}

void
neut_mesh_tri_prism (struct NODES* pNodes, struct MESH Mesh,
		     struct MESH *pPMesh, double thickness)
{
  int i, j;
  double coo[3];
  int NodeQty = (*pNodes).NodeQty;

  if (Mesh.Dimension != 2)
    ut_error_reportbug ();

  (*pPMesh).EltType = ut_alloc_1d_char (6);
  strcpy ((*pPMesh).EltType, "prism");
  (*pPMesh).Dimension = 3;
  (*pPMesh).EltOrder = 1;

  (*pPMesh).EltQty = Mesh.EltQty;
  (*pPMesh).EltNodes = ut_alloc_2d_int ((*pPMesh).EltQty + 1, 6);

  (*pPMesh).EltElset = ut_alloc_1d_int ((*pPMesh).EltQty + 1);

  for (i = 1; i <= Mesh.EltQty; i++)
  {
    for (j = 0; j < 3; j++)
    {
      (*pPMesh).EltNodes[i][j] = Mesh.EltNodes[i][j];
      (*pPMesh).EltNodes[i][j] = Mesh.EltNodes[i][j] + (*pNodes).NodeQty;
    }
  }

  for (i = 1; i <=  NodeQty; i++)
  {
    coo[0] = (*pNodes).NodeCoo[i][0];
    coo[1] = (*pNodes).NodeCoo[i][1];
    coo[2] = (*pNodes).NodeCoo[i][2] + thickness;
    neut_nodes_addnode(pNodes, coo, 0);
  }

  neut_mesh_init_elsets (pPMesh);

  neut_mesh_init_nodeelts (pPMesh, (*pNodes).NodeQty);

  return;
}

void
neut_mesh_tri_tet (struct NODES *pNodes, struct MESH Mesh,
                   struct MESH *pTMesh, double thickness)
{
  int i, j, k;
  int NodeQty_ini = (*pNodes).NodeQty;
  int indir[6],prismnodes[6], newelt;
  double coo[3];

  if (Mesh.Dimension != 2)
    ut_error_reportbug ();

  int *tet[3] = {NULL, NULL, NULL};
  int tet1_1[4] = {0, 1, 2, 5};
  int tet2_1[4] = {0, 1, 5, 4};
  int tet3_1[4] = {0, 4, 5, 3};
  int tet1_2[4] = {0, 1, 2 ,4};
  int tet2_2[4] = {0, 4, 2, 5};
  int tet3_2[4] = {0, 4, 5, 3};

  if (strcmp (Mesh.EltType, "tri") != 0 || Mesh.Dimension != 2)
    ut_error_reportbug ();

  // add nodes

  for (i = 1; i <=  NodeQty_ini; i++)
  {
    coo[0] = (*pNodes).NodeCoo[i][0];
    coo[1] = (*pNodes).NodeCoo[i][1];
    coo[2] = (*pNodes).NodeCoo[i][2] + thickness;
    neut_nodes_addnode(pNodes, coo, 0);
  }

  // subdivide into tet

  (*pTMesh).EltType = ut_alloc_1d_char (4);
  strcpy ((*pTMesh).EltType, "tri");
  (*pTMesh).Dimension = 3;
  (*pTMesh).EltOrder = 1;

  (*pTMesh).EltQty = 0;
  (*pTMesh).EltNodes = ut_alloc_2d_int (3 * Mesh.EltQty + 1, 4);

  (*pTMesh).EltElset = ut_alloc_1d_int (3 * Mesh.EltQty + 1);

  for (i = 1; i <= Mesh.EltQty; i++)
  {
    for (k = 0; k < 3; k++)
    {
      prismnodes[k] = Mesh.EltNodes[i][k];
      prismnodes[k+3] = prismnodes[k] + NodeQty_ini;
    }

    // find minimum vertex and set indirection
    if (prismnodes[0] < prismnodes[1] && prismnodes[0] < prismnodes[2])
    {
     indir[0] = 0;indir[1] = 1;indir[2] = 2;indir[3] = 3;indir[4] = 4;indir[5] = 5;
    }
    else if (prismnodes[1] < prismnodes[0] && prismnodes[1] < prismnodes[2])
    {
     indir[0] = 1;indir[1] = 2;indir[2] = 0;indir[3] = 4;indir[4] = 5;indir[5] = 3;
    }
    else if (prismnodes[2] < prismnodes[0] && prismnodes[2] < prismnodes[1])
    {
     indir[0] = 2;indir[1] = 0;indir[2] = 1;indir[3] = 5;indir[4] = 3;indir[5] = 4;
    }

    // select subdivision manner
    if (prismnodes[indir[1]] < prismnodes[indir[2]])
    {
      tet[0] = tet1_1; tet[1] = tet2_1; tet[2] = tet3_1;
    }
    else if (prismnodes[indir[1]] > prismnodes[indir[2]])
    {
      tet[0] = tet1_2; tet[1] = tet2_2; tet[2] = tet3_2;
    }
    else
      ut_error_reportbug ();

    // fill MESH
    for (k = 0; k < 3; k++)
    {
      newelt = ++((*pTMesh).EltQty);
      for (j = 0; j < 4; j++)
      {
	 (*pTMesh).EltNodes[newelt][j] =  prismnodes[indir[tet[k][j]]];
      }

      (*pTMesh).EltElset[newelt] = Mesh.EltElset[i];
    }
  }

  neut_mesh_init_elsets (pTMesh);

  neut_mesh_init_nodeelts (pTMesh, (*pNodes).NodeQty);

  return;
}

int
neut_mesh_rmelts (struct MESH *pMesh, struct NODES Nodes, int *rmelt,
		  int rmeltqty)
{
  int i, j, eltmin, eltmax, eltqty;
  int *new_old = ut_alloc_1d_int ((*pMesh).EltQty + 1);
  int *old_new = ut_alloc_1d_int ((*pMesh).EltQty + 1);
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  ut_array_1d_int_sort (rmelt, rmeltqty);
  eltmin = rmelt[0];
  eltmax = rmelt[rmeltqty - 1];

  eltqty = 0;
  for (i = 1; i < eltmin; i++)
    new_old[++eltqty] = i;

  if ((eltmax - eltmin + 1) != rmeltqty)
    for (i = eltmin; i <= eltmax; i++)
      if (ut_array_1d_int_eltpos (rmelt, rmeltqty, i) == -1)
	new_old[++eltqty] = i;

  for (i = eltmax + 1; i <= (*pMesh).EltQty; i++)
    new_old[++eltqty] = i;

  for (i = 1; i <= (*pMesh).EltQty; i++)
    old_new[new_old[i]] = i;

  // Updating EltNodes
  for (i = 1; i <= eltqty; i++)
    ut_array_1d_int_memcpy ((*pMesh).EltNodes[i], eltnodeqty,
			    (*pMesh).EltNodes[new_old[i]]);
  (*pMesh).EltNodes =
    ut_realloc_2d_int_delline ((*pMesh).EltNodes, (*pMesh).EltQty + 1,
			       eltqty + 1);

  // Updating EltElset
  if ((*pMesh).EltElset != NULL)
    for (i = 1; i <= eltqty; i++)
      (*pMesh).EltElset[i] = (*pMesh).EltElset[new_old[i]];
  (*pMesh).EltElset = ut_realloc_1d_int ((*pMesh).EltElset, eltqty + 1);

  // Updating NodeElts
  if ((*pMesh).NodeElts != NULL)
  {
    ut_free_2d_int ((*pMesh).NodeElts, Nodes.NodeQty + 1);
    neut_mesh_init_nodeelts (pMesh, Nodes.NodeQty);
  }

  // Updating Elsets
  for (i = 1; i <= (*pMesh).ElsetQty; i++)
    for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
      (*pMesh).Elsets[i][j] = old_new[(*pMesh).Elsets[i][j]];

  (*pMesh).EltQty = eltqty;

  ut_free_1d_int (new_old);
  ut_free_1d_int (old_new);

  return 0;
}

int
neut_mesh_rmelt (struct MESH *pMesh, int elt)
{
  int i, elset, status, node;
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  elset = (*pMesh).EltElset[elt];

  // Updating EltNodes
  for (i = elt; i < (*pMesh).EltQty; i++)
    ut_array_1d_int_memcpy ((*pMesh).EltNodes[i], eltnodeqty,
			    (*pMesh).EltNodes[i + 1]);

  // Updating Elsets
  status = ut_array_1d_int_deletencompress
    ((*pMesh).Elsets[elset] + 1, (*pMesh).Elsets[elset][0], elt, 1);
  if (status != 1)
    ut_error_reportbug ();
  else
    (*pMesh).Elsets[elset][0]--;

  // Updating EltElset
  if ((*pMesh).EltElset != NULL)
    for (i = elt; i < (*pMesh).EltQty; i++)
      (*pMesh).EltElset[i] = (*pMesh).EltElset[i + 1];
  (*pMesh).EltElset = ut_realloc_1d_int ((*pMesh).EltElset, (*pMesh).EltQty);

  // Updating NodeElts
  if ((*pMesh).NodeElts != NULL)
    for (i = 0; i < eltnodeqty; i++)
    {
      node = (*pMesh).EltNodes[elt][i];
      status = ut_array_1d_int_deletencompress
	((*pMesh).NodeElts[node] + 1, (*pMesh).NodeElts[node][0], elt, 1);
      if (status != 1)
	ut_error_reportbug ();
      (*pMesh).NodeElts[node][0]--;
    }

  (*pMesh).EltQty--;

  return 0;
}

int
neut_mesh_rmelset (struct MESH *pMesh, struct NODES Nodes, int elset)
{
  int i, j, eltqty;
  int *new_old = ut_alloc_1d_int ((*pMesh).EltQty + 1);
  int *old_new = ut_alloc_1d_int ((*pMesh).EltQty + 1);
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  ut_array_1d_int_sort ((*pMesh).Elsets[elset] + 1,
			(*pMesh).Elsets[elset][0]);

  eltqty = 0;
  for (i = 1; i <= (*pMesh).EltQty; i++)
    if ((*pMesh).EltElset[i] != elset)
      new_old[++eltqty] = i;

  for (i = 1; i <= (*pMesh).EltQty; i++)
    old_new[new_old[i]] = i;

  // Updating EltNodes
  for (i = 1; i <= eltqty; i++)
    ut_array_1d_int_memcpy ((*pMesh).EltNodes[i], eltnodeqty,
			    (*pMesh).EltNodes[new_old[i]]);
  (*pMesh).EltNodes =
    ut_realloc_2d_int_delline ((*pMesh).EltNodes, (*pMesh).EltQty + 1,
			       eltqty + 1);

  // Updating EltElset
  if ((*pMesh).EltElset != NULL)
    for (i = 1; i <= eltqty; i++)
      (*pMesh).EltElset[i] = (*pMesh).EltElset[new_old[i]];
  (*pMesh).EltElset = ut_realloc_1d_int ((*pMesh).EltElset, eltqty + 1);

  // Updating Elsets
  (*pMesh).Elsets[elset] = ut_realloc_1d_int ((*pMesh).Elsets[elset], 1);
  (*pMesh).Elsets[elset][0] = 0;

  for (i = 1; i <= (*pMesh).ElsetQty; i++)
    for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
      (*pMesh).Elsets[i][j] = old_new[(*pMesh).Elsets[i][j]];

  (*pMesh).EltQty = eltqty;

  // Updating NodeElts
  if ((*pMesh).NodeElts != NULL)
  {
    ut_free_2d_int ((*pMesh).NodeElts, Nodes.NodeQty + 1);
    neut_mesh_init_nodeelts (pMesh, Nodes.NodeQty);
  }

  ut_free_1d_int (new_old);
  ut_free_1d_int (old_new);

  return 0;
}

// Ordering elements to all point towards the same side of the face
// (all topological, no buggy normal determination)
void
neut_mesh_orderelsets (struct MESH *pMesh)
{
  int i, j, elt, neltqty;
  int *id = NULL;
  int qty;
  int *elt_ref = NULL;
  int *nelts = NULL;
  int *elts = ut_alloc_1d_int (2);
  int *nodes = NULL;
  int nodeqty;
  int ori1, ori2;
  int *tmp = NULL;

  if ((*pMesh).Dimension != 2)
    ut_error_reportbug ();

  for (i = 1; i <= (*pMesh).ElsetQty; i++)
  {
    id = ut_alloc_1d_int ((*pMesh).Elsets[i][0]);
    elt_ref = ut_alloc_1d_int ((*pMesh).EltQty + 1);

    id[0] = (*pMesh).Elsets[i][1];
    elt = id[0];
    qty = 1;
    neut_mesh_elt_elset_neighelts (*pMesh, elt, i, &tmp, &neltqty);
    ut_array_1d_int_memcpy (id + qty, neltqty, tmp);
    ut_free_1d_int (tmp);
    tmp = NULL;

    qty += neltqty;
    for (j = 0; j < qty; j++)
      elt_ref[id[j]] = elt;

    int pos = 1;
    while (pos < qty)
    {
      elt = id[pos];

      // checking element orientation
      elts[0] = elt_ref[elt];
      elts[1] = elt;
      neut_mesh_elts_comnodes (*pMesh, elts, 2, &nodes, &nodeqty);
      if (nodeqty < 2)
	ut_error_reportbug ();

      neut_mesh_elt_nodes_ori (*pMesh, elts[0], nodes, &ori1);
      neut_mesh_elt_nodes_ori (*pMesh, elts[1], nodes, &ori2);

      if (ori1 == 0 || ori2 == 0)
      {
	neut_debug_mesh (stdout, *pMesh);
	printf ("elts = %d %d\n", elts[0], elts[1]);
	printf ("nodes = %d %d\n", nodes[0], nodes[1]);
	printf ("ori1 = %d ori2 = %d\n", ori1, ori2);
	ut_error_reportbug ();
      }

      if (ori1 == ori2)
	neut_mesh_elt_reversenodes (pMesh, elt);

      // recording element neighbours
      neut_mesh_elt_elset_neighelts (*pMesh, elt, i, &nelts, &neltqty);
      for (j = 0; j < neltqty; j++)
	if (elt_ref[nelts[j]] == 0)
	{
	  elt_ref[nelts[j]] = elt;
	  id[qty++] = nelts[j];
	}

      pos++;
    }

    ut_free_1d_int (elt_ref);
  }

  ut_free_1d_int (nelts);
  ut_free_1d_int (elts);
  ut_free_1d_int (nodes);

  return;
}

void
neut_mesh_addelt (struct MESH *pMesh, int *EltNodes)
{
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  (*pMesh).EltQty++;

  (*pMesh).EltNodes =
    ut_realloc_1d_pint ((*pMesh).EltNodes, (*pMesh).EltQty + 1);
  (*pMesh).EltNodes[(*pMesh).EltQty] = ut_alloc_1d_int (eltnodeqty);
  (*pMesh).EltNodes[0] = NULL;	/* mandatory */

  ut_array_1d_int_memcpy ((*pMesh).EltNodes[(*pMesh).EltQty], eltnodeqty,
			  EltNodes);

  /* update NodeElts */
  /* if ((*pMesh).NodeElts != NULL) */

  return;
}

void
neut_mesh_addelset (struct MESH *pMesh, int *ElsetElts, int EltQty)
{
  int i;

  (*pMesh).ElsetQty++;

  (*pMesh).Elsets =
    ut_realloc_1d_pint ((*pMesh).Elsets, (*pMesh).ElsetQty + 1);
  (*pMesh).Elsets[0] = NULL;	/* mandatory */

  (*pMesh).Elsets[(*pMesh).ElsetQty] = ut_alloc_1d_int (EltQty + 1);
  (*pMesh).Elsets[(*pMesh).ElsetQty][0] = EltQty;
  if (EltQty == (*pMesh).EltQty || ElsetElts == NULL)
    for (i = 1; i <= EltQty; i++)
      (*pMesh).Elsets[(*pMesh).ElsetQty][i] = i;
  else
    ut_array_1d_int_memcpy ((*pMesh).Elsets[(*pMesh).ElsetQty] + 1, EltQty,
			    ElsetElts);

  return;
}

void
neut_mesh_elset_addelt (struct MESH *pMesh, int elset_nb, int elt)
{
  (*pMesh).Elsets[elset_nb][0]++;

  (*pMesh).Elsets[elset_nb] = ut_realloc_1d_int
    ((*pMesh).Elsets[elset_nb], (*pMesh).Elsets[elset_nb][0] + 1);

  (*pMesh).Elsets[elset_nb][(*pMesh).Elsets[elset_nb][0]] = elt;

  return;
}


void
neut_mesh_elt_reversenodes (struct MESH *pMesh, int eltnb)
{
  // implemented for 1D and 2D only.

  // For 2D, the 1st and 2nd order nodes are to be considered separetely
  // for reversing.
  if ((*pMesh).Dimension == 1)
  {
    ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb], 2);
  }
  else if ((*pMesh).Dimension == 2)
  {
    if (!strcmp ((*pMesh).EltType, "tri"))
    {
      if ((*pMesh).EltOrder == 1)
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 1, 2);
      else if ((*pMesh).EltOrder == 2)
      {
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 1, 2);
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 3, 3);
      }
      else
	abort ();
    }
    else if (!strcmp ((*pMesh).EltType, "quad"))
    {
      if ((*pMesh).EltOrder == 1)
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 1, 3);
      else if ((*pMesh).EltOrder == 2)
      {
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 1, 3);
	ut_array_1d_int_reverseelts ((*pMesh).EltNodes[eltnb] + 4, 4);
      }
      else
	abort ();
    }
    else
      ut_error_reportbug ();
  }
  else
    abort ();

  return;
}

void
neut_mesh_elts_switch_pair (struct MESH *pMesh, int n1, int n2)
{
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  ut_array_2d_int_switchlines ((*pMesh).EltNodes, eltnodeqty, n1, n2);

  return;
}

/* RAM could be improved by more elegant exchange of lines (not through
 * a big copy eltnodes array */
void
neut_mesh_elts_switch (struct MESH *pMesh, int *elt_nbs)
{
  int i, j;
  int **eltnodes = NULL;
  int eltnodeqty = neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
				     (*pMesh).EltOrder);

  eltnodes = ut_alloc_2d_int ((*pMesh).EltQty + 1, eltnodeqty);

  for (i = 1; i <= elt_nbs[0]; i++)
    ut_array_1d_int_memcpy (eltnodes[i], eltnodeqty, (*pMesh).EltNodes[i]);

  for (i = 1; i <= (*pMesh).EltQty; i++)
    ut_array_1d_int_memcpy ((*pMesh).EltNodes[elt_nbs[i]],
			    eltnodeqty, eltnodes[i]);

  for (i = 1; i <= (*pMesh).ElsetQty; i++)
    for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
      (*pMesh).Elsets[i][j] = elt_nbs[(*pMesh).Elsets[i][j]];

  if ((*pMesh).NodeElts != NULL)
  {
    int nodeqty = (*pMesh).NodeQty;
    ut_free_2d_int ((*pMesh).NodeElts, nodeqty + 1);
    neut_mesh_init_nodeelts (pMesh, nodeqty);

    // the code below would be a little faster but does not work
    /*
       for (i = 1; i <= (*pMesh).NodeQty; i++)
       for (j = 1; j <= (*pMesh).NodeElts[i][0]; j++)
       (*pMesh).NodeElts[i][j] = elt_nbs[(*pMesh).NodeElts[i][j]];
     */
  }

  ut_free_2d_int (eltnodes, (*pMesh).EltQty);

  return;
}

/* node_nbs[...] = node qty then new pos */
void
neut_mesh_nodes_switch (struct MESH *pMesh, int *node_nbs)
{
  int i;
  int j, eltnodeqty;

  eltnodeqty =
    neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
		      (*pMesh).EltOrder);

  /* neut_mesh_switch (pMesh, node_nbs, NULL); */
  for (i = 1; i <= (*pMesh).EltQty; i++)
    for (j = 0; j < eltnodeqty; j++)
      (*pMesh).EltNodes[i][j] = node_nbs[(*pMesh).EltNodes[i][j]];

  return;
}

void
neut_mesh_switch (struct MESH *pMesh, int *nodes_old_new, int *elts_old_new,
		  int *elsets_old_new)
{
  int i, j, eltnodeqty;

  /* correcting elt nodes */
  if (nodes_old_new != NULL)
  {
    eltnodeqty =
      neut_elt_nodeqty ((*pMesh).EltType, (*pMesh).Dimension,
			(*pMesh).EltOrder);

    for (i = 1; i <= (*pMesh).EltQty; i++)
      for (j = 0; j < eltnodeqty; j++)
	(*pMesh).EltNodes[i][j] = nodes_old_new[(*pMesh).EltNodes[i][j]];
  }

  /* correcting elset elts */
  if (elts_old_new != NULL)
    for (i = 1; i <= (*pMesh).ElsetQty; i++)
      for (j = 1; j <= (*pMesh).Elsets[i][0]; j++)
	(*pMesh).Elsets[i][j] = elts_old_new[(*pMesh).Elsets[i][j]];

  /* correcting elt elsets */
  if (elsets_old_new != NULL)
  {
    for (i = 1; i <= (*pMesh).EltQty; i++)
      (*pMesh).EltElset[i] = elsets_old_new[(*pMesh).EltElset[i]];

    if ((*pMesh).ElsetId)
      ut_error_reportbug ();
  }

  return;
}

void
neut_mesh_renumber_continuous (struct MESH *pMesh, int *node_nbs,
			       int *elt_nbs, int *elset_nbs)
{
  int i, elt_nb_max, elset_nb_max;
  int *nodes_old_new = NULL;
  int *elts_old_new = NULL;
  int *elsets_old_new = NULL;

  int node_nb_max = ut_array_1d_int_max (node_nbs + 1, node_nbs[0]);

  if (node_nbs != NULL)
  {
    nodes_old_new = ut_alloc_1d_int (node_nb_max + 1);
    for (i = 1; i <= node_nbs[0]; i++)
      nodes_old_new[node_nbs[i]] = i;
  }

  if (elt_nbs != NULL)
  {
    elt_nb_max = ut_array_1d_int_max (elt_nbs + 1, (*pMesh).EltQty);

    elts_old_new = ut_alloc_1d_int (elt_nb_max + 1);
    for (i = 1; i <= (*pMesh).EltQty; i++)
      elts_old_new[elt_nbs[i]] = i;

    elset_nb_max = ut_array_1d_int_max (elset_nbs + 1, (*pMesh).ElsetQty);

    elsets_old_new = ut_alloc_1d_int (elset_nb_max + 1);
    for (i = 1; i <= elset_nbs[0]; i++)
      elsets_old_new[elset_nbs[i]] = i;

    neut_mesh_switch (pMesh, nodes_old_new, elts_old_new, elsets_old_new);
  }

  ut_free_1d_int (nodes_old_new);
  ut_free_1d_int (elts_old_new);
  ut_free_1d_int (elsets_old_new);

  return;
}

void
neut_mesh_elt_split (struct NODES *pNodes, struct MESH *pMesh, int elt)
{
  double *coo = ut_alloc_1d (3);
  int *nodes = ut_alloc_1d_int (3);
  int newnode, newelt1, newelt2;

  if ((*pMesh).Dimension != 2)
    ut_error_reportbug ();

  if ((*pMesh).NodeElts == NULL)
    ut_error_reportbug ();

  ut_array_1d_int_memcpy (nodes, 3, (*pMesh).EltNodes[elt]);

  neut_mesh_elt_centre (*pNodes, *pMesh, elt, coo);
  neut_nodes_addnode (pNodes, coo, -1);
  newnode = (*pNodes).NodeQty;
  (*pMesh).NodeQty = (*pNodes).NodeQty;

  neut_mesh_addelt (pMesh, nodes);
  neut_mesh_addelt (pMesh, nodes);
  newelt1 = (*pMesh).EltQty - 1;
  newelt2 = (*pMesh).EltQty;
  neut_mesh_elset_addelt (pMesh, (*pMesh).EltElset[elt], newelt1);
  neut_mesh_elset_addelt (pMesh, (*pMesh).EltElset[elt], newelt2);
  (*pMesh).EltElset
    = ut_realloc_1d_int ((*pMesh).EltElset, (*pMesh).EltQty + 1);
  (*pMesh).EltElset[newelt1] = (*pMesh).EltElset[elt];
  (*pMesh).EltElset[newelt2] = (*pMesh).EltElset[elt];

  ut_array_1d_int_rotate ((*pMesh).EltNodes[newelt1], 3, 1);
  ut_array_1d_int_rotate ((*pMesh).EltNodes[newelt2], 3, 2);

  (*pMesh).EltNodes[elt][2] = newnode;
  (*pMesh).EltNodes[newelt1][2] = newnode;
  (*pMesh).EltNodes[newelt2][2] = newnode;

  (*pMesh).NodeElts[nodes[0]][0]++;
  (*pMesh).NodeElts[nodes[0]]
    = ut_realloc_1d_int ((*pMesh).NodeElts[nodes[0]],
			 (*pMesh).NodeElts[nodes[0]][0] + 1);
  (*pMesh).NodeElts[nodes[0]][(*pMesh).NodeElts[nodes[0]][0]] = newelt2;

  (*pMesh).NodeElts[nodes[1]][0]++;
  (*pMesh).NodeElts[nodes[1]]
    = ut_realloc_1d_int ((*pMesh).NodeElts[nodes[1]],
			 (*pMesh).NodeElts[nodes[1]][0] + 1);
  (*pMesh).NodeElts[nodes[1]][(*pMesh).NodeElts[nodes[1]][0]] = newelt1;

  (*pMesh).NodeElts[nodes[2]][0]++;
  (*pMesh).NodeElts[nodes[2]]
    = ut_realloc_1d_int ((*pMesh).NodeElts[nodes[2]],
			 (*pMesh).NodeElts[nodes[2]][0] + 1);
  (*pMesh).NodeElts[nodes[2]][(*pMesh).NodeElts[nodes[2]][0]] = newelt2;

  ut_array_1d_int_findnreplace ((*pMesh).NodeElts[nodes[2]] + 1,
				(*pMesh).NodeElts[nodes[2]][0], elt, newelt1);

  (*pMesh).NodeElts = ut_realloc_2d_int_addline ((*pMesh).NodeElts,
						 (*pMesh).NodeQty + 1, 4);
  (*pMesh).NodeElts[newnode][0] = 3;
  (*pMesh).NodeElts[newnode][1] = elt;
  (*pMesh).NodeElts[newnode][2] = newelt1;
  (*pMesh).NodeElts[newnode][3] = newelt2;

  /*
     if (neut_mesh_test (*pMesh) != 0)
     ut_error_reportbug ();
   */

  ut_free_1d (coo);
  ut_free_1d_int (nodes);

  return;
}

void
neut_mesh_eltpair_flip (struct MESH *pMesh, int elt1, int elt2)
{
  int i;
  int elt1change, elt1mine, elt1keep;
  int elt2mine, elt2keep;
  int elt1change_pos, elt1mine_pos, elt1keep_pos;
  int elt2change_pos, elt2mine_pos, elt2keep_pos;

  if ((*pMesh).Dimension != 2 || (*pMesh).EltOrder != 1)
    ut_error_reportbug ();

  // looking for the nodes belonging to elt1 and elt2 only, respectively
  elt1mine_pos = -1;
  for (i = 0; i < 3; i++)
    if (ut_array_1d_int_eltpos
	((*pMesh).EltNodes[elt2], 3, (*pMesh).EltNodes[elt1][i]) == -1)
    {
      elt1mine_pos = i;
      break;
    }

  if (elt1mine_pos == -1)
    abort ();

  elt1mine = (*pMesh).EltNodes[elt1][elt1mine_pos];

  elt2mine_pos = -1;
  for (i = 0; i < 3; i++)
    if (ut_array_1d_int_eltpos
	((*pMesh).EltNodes[elt1], 3, (*pMesh).EltNodes[elt2][i]) == -1)
    {
      elt2mine_pos = i;
      break;
    }

  if (elt2mine_pos == -1)
    abort ();

  elt2mine = (*pMesh).EltNodes[elt2][elt2mine_pos];

  // picking a node to keep in elt1
  elt1keep_pos = (elt1mine_pos != 0) ? 0 : 1;
  elt1keep = (*pMesh).EltNodes[elt1][elt1keep_pos];

  // the node to change is the other one...
  elt1change_pos = 3 - (elt1mine_pos + elt1keep_pos);
  elt1change = (*pMesh).EltNodes[elt1][elt1change_pos];

  // in elt2, keeping the node that elt1 changes
  elt2keep_pos =
    ut_array_1d_int_eltpos ((*pMesh).EltNodes[elt2], 4, elt1change);
  elt2keep = (*pMesh).EltNodes[elt2][elt2keep_pos];

  // the node to change is the other one...
  elt2change_pos = 3 - (elt2mine_pos + elt2keep_pos);

  // changing the nodes for the node that only the other elt had
  (*pMesh).EltNodes[elt1][elt1change_pos] = elt2mine;
  (*pMesh).EltNodes[elt2][elt2change_pos] = elt1mine;

  // updating NodeElts
  (*pMesh).NodeElts[elt1mine][0]++;
  (*pMesh).NodeElts[elt1mine]
    = ut_realloc_1d_int ((*pMesh).NodeElts[elt1mine],
			 (*pMesh).NodeElts[elt1mine][0] + 1);
  (*pMesh).NodeElts[elt1mine][(*pMesh).NodeElts[elt1mine][0]] = elt2;

  (*pMesh).NodeElts[elt2mine][0]++;
  (*pMesh).NodeElts[elt2mine]
    = ut_realloc_1d_int ((*pMesh).NodeElts[elt2mine],
			 (*pMesh).NodeElts[elt2mine][0] + 1);
  (*pMesh).NodeElts[elt2mine][(*pMesh).NodeElts[elt2mine][0]] = elt1;

  if (ut_array_1d_int_deletencompress ((*pMesh).NodeElts[elt1keep] + 1,
				       (*pMesh).NodeElts[elt1keep][0],
				       elt2, 1) != 1)
    ut_error_reportbug ();

  (*pMesh).NodeElts[elt1keep][0]--;

  (*pMesh).NodeElts[elt1keep]
    =
    ut_realloc_1d_int ((*pMesh).NodeElts[elt1keep],
		       (*pMesh).NodeElts[elt1keep][0] + 1);

  if (ut_array_1d_int_deletencompress ((*pMesh).NodeElts[elt2keep] + 1,
				       (*pMesh).NodeElts[elt2keep][0],
				       elt1, 1) != 1)
    ut_error_reportbug ();

  (*pMesh).NodeElts[elt2keep][0]--;

  (*pMesh).NodeElts[elt2keep]
    =
    ut_realloc_1d_int ((*pMesh).NodeElts[elt2keep],
		       (*pMesh).NodeElts[elt2keep][0] + 1);

  /*
     if (neut_mesh_test (*pMesh) != 0)
     ut_error_reportbug ();
   */

  return;
}

void
neut_mesh_eltpair_split (struct NODES *pNodes, struct MESH *pMesh, int elt1,
			 int elt2)
{
  int i, j, elt, node;
  int newnode;
  int *elts = ut_alloc_1d_int (2);
  int *newelts = ut_alloc_1d_int (2);
  int *allelts = ut_alloc_1d_int (4);
  int *allnodes = NULL;
  int *comnodes = NULL;
  int allnodeqty;
  int comnodeqty;
  double *bary = ut_alloc_1d (3);

  if ((*pMesh).Dimension != 2 || (*pMesh).EltOrder != 1)
    ut_error_reportbug ();

  elts[0] = elt1;
  elts[1] = elt2;

  neut_mesh_elts_allnodes (*pMesh, elts, 2, &allnodes, &allnodeqty);
  neut_mesh_elts_comnodes (*pMesh, elts, 2, &comnodes, &comnodeqty);

  ut_array_1d_add ((*pNodes).NodeCoo[comnodes[0]],
		   (*pNodes).NodeCoo[comnodes[1]], 3, bary);
  ut_array_1d_scale (bary, 3, .5);

  // Rotating EltNodes so that the node not shared with the other elt is at [0].
  for (i = 0; i < 2; i++)
    for (j = 0; j < 3; j++)
      if (ut_array_1d_int_eltpos
	  (comnodes, comnodeqty, (*pMesh).EltNodes[elts[i]][j]) == -1)
      {
	ut_array_1d_int_rotate ((*pMesh).EltNodes[elts[i]], 3, j);
	break;
      }

  neut_nodes_addnode (pNodes, bary, -1);
  newnode = (*pNodes).NodeQty;
  (*pMesh).NodeQty = (*pNodes).NodeQty;

  for (i = 0; i < 2; i++)
  {
    elt = elts[i];
    neut_mesh_addelt (pMesh, (*pMesh).EltNodes[elt]);
    newelts[i] = (*pMesh).EltQty;
    neut_mesh_elset_addelt (pMesh, (*pMesh).EltElset[elt], newelts[i]);
    (*pMesh).EltElset = ut_realloc_1d_int ((*pMesh).EltElset,
					   (*pMesh).EltQty + 1);
    (*pMesh).EltElset[newelts[i]] = (*pMesh).EltElset[elt];

    if (i == 0)
    {
      (*pMesh).EltNodes[elt][2] = newnode;
      (*pMesh).EltNodes[newelts[i]][1] = newnode;
    }
    else
    {
      (*pMesh).EltNodes[elt][1] = newnode;
      (*pMesh).EltNodes[newelts[i]][2] = newnode;
    }
  }
  ut_array_1d_int_memcpy (allelts, 2, elts);
  ut_array_1d_int_memcpy (allelts + 2, 2, newelts);

  // cleaning NodeElts[comnodes[]] from elt1 and elt2, and
  // reinitializing them

  for (i = 0; i < 4; i++)
  {
    node = allnodes[i];

    for (j = 0; j < 2; j++)
      (*pMesh).NodeElts[node][0] -=
	ut_array_1d_int_deletencompress ((*pMesh).NodeElts[node] + 1,
					 (*pMesh).NodeElts[node][0], elts[j],
					 1);

    for (j = 0; j < 4; j++)
    {
      elt = allelts[j];
      if (ut_array_1d_int_eltpos ((*pMesh).EltNodes[elt], 3, node) != -1)
      {
	(*pMesh).NodeElts[node][0]++;
	(*pMesh).NodeElts[node] = ut_realloc_1d_int
	  ((*pMesh).NodeElts[node], (*pMesh).NodeElts[node][0] + 1);
	(*pMesh).NodeElts[node][(*pMesh).NodeElts[node][0]] = elt;
      }
    }
  }

  // Setting NodeElts for the new node

  (*pMesh).NodeElts
    = ut_realloc_2d_int_addline ((*pMesh).NodeElts, (*pNodes).NodeQty + 1, 5);

  (*pMesh).NodeElts[newnode][0] = 4;
  (*pMesh).NodeElts[newnode][1] = elt1;
  (*pMesh).NodeElts[newnode][2] = elt2;
  (*pMesh).NodeElts[newnode][3] = (*pMesh).EltQty - 1;
  (*pMesh).NodeElts[newnode][4] = (*pMesh).EltQty;

  /*
     if (neut_mesh_test (*pMesh) != 0)
     ut_error_reportbug ();
   */

  ut_free_1d_int (elts);
  ut_free_1d_int (newelts);
  ut_free_1d_int (allelts);
  ut_free_1d_int (allnodes);
  ut_free_1d_int (comnodes);

  return;
}
