///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// TetGen                                                                    //
//                                                                           //
// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator       //
//                                                                           //
// Version 1.5                                                               //
// (June 21, 2013)                                                           //
//                                                                           //
// Copyright (C) 2002--2013                                                  //
// Hang Si                                                                   //
// Research Group: Numerical Mathematics and Scientific Computing            //
// Weierstrass Institute for Applied Analysis and Stochastics (WIAS)         //
// Mohrenstr. 39, 10117 Berlin, Germany                                      //
// si@wias-berlin.de                                                         //
//                                                                           //
// TetGen is freely available through the website: http://www.tetgen.org.    //
//   It may be copied, modified, and redistributed for non-commercial use.   //
//   Please consult the file LICENSE for the detailed copyright notices.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "tetgen.h"

// [Laurent Alonso, Bruno] replaced all instance of NULL by nullptr

// [Bruno] redirected output into Geogram terminal
#include <geogram/basic/logger.h>
#define printf geogram_printf

// [Bruno] using PCK predicates
#define USE_PCK

// [Bruno] disable some warnings for ICC
#ifdef __ICC
#pragma warning disable 869 593
#endif

namespace GEO_3rdParty { // [Bruno]

//// io_cxx ///////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node_call()    Read a list of points from a file.                    //
//                                                                           //
// 'infile' is the file handle contains the node list.  It may point to a    //
// .node, or .poly or .smesh file. 'markers' indicates each node contains an //
// additional marker (integer) or not. 'uvflag' indicates each node contains //
// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name //
// of the file being read,  it is only used in error messages.               //
//                                                                           //
// The 'firstnumber' (0 or 1) is automatically determined by the number of   //
// the first index of the first point.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, 
                              char* infilename)
{
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL x, y, z, attrib;
  int firstnode, currentmarker;
  int index, attribindex;
  int i, j;

  // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'.
  pointlist = new REAL[numberofpoints * 3];
  if (pointlist == (REAL *) nullptr) {
    terminatetetgen(1);
  }
  if (numberofpointattributes > 0) {
    pointattributelist = new REAL[numberofpoints * numberofpointattributes];
    if (pointattributelist == (REAL *) nullptr) {
      terminatetetgen(1);
    }
  }
  if (markers) {
    pointmarkerlist = new int[numberofpoints];
    if (pointmarkerlist == (int *) nullptr) {
      terminatetetgen(1);
    }
  }
  if (uvflag) {
    pointparamlist = new pointparam[numberofpoints];
    if (pointparamlist == nullptr) {
      terminatetetgen(1);
    }
  }

  // Read the point section.
  index = 0;
  attribindex = 0;
  for (i = 0; i < numberofpoints; i++) {
    stringptr = readnumberline(inputline, infile, infilename);
    if (useindex) {
      if (i == 0) {
        firstnode = (int) strtol (stringptr, &stringptr, 0);
        if ((firstnode == 0) || (firstnode == 1)) {
          firstnumber = firstnode;
        }
      }
      stringptr = findnextnumber(stringptr);
    } // if (useindex)
    if (*stringptr == '\0') {
      printf("Error:  Point %d has no x coordinate.\n", firstnumber + i);
      break;
    }
    x = (REAL) strtod(stringptr, &stringptr);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
      break;
    }
    y = (REAL) strtod(stringptr, &stringptr);
    if (mesh_dim == 3) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no z coordinate.\n", firstnumber + i);
        break;
      }
      z = (REAL) strtod(stringptr, &stringptr);
    } else {
      z = 0.0; // mesh_dim == 2;
    }
    pointlist[index++] = x;
    pointlist[index++] = y;
    pointlist[index++] = z;
    // Read the point attributes.
    for (j = 0; j < numberofpointattributes; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        attrib = 0.0;
      } else {
        attrib = (REAL) strtod(stringptr, &stringptr);
      }
      pointattributelist[attribindex++] = attrib;
    }
    if (markers) {
      // Read a point marker.
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        currentmarker = 0;
      } else {
        currentmarker = (int) strtol (stringptr, &stringptr, 0);
      }
      pointmarkerlist[i] = currentmarker;
    }
    if (uvflag) {
      // Read point paramteters.
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no uv[0].\n", firstnumber + i);
        break;
      }
      pointparamlist[i].uv[0] = (REAL) strtod(stringptr, &stringptr);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no uv[1].\n", firstnumber + i);
        break;
      }
      pointparamlist[i].uv[1] = (REAL) strtod(stringptr, &stringptr);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no tag.\n", firstnumber + i);
        break;
      }
      pointparamlist[i].tag = (int) strtol (stringptr, &stringptr, 0);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no type.\n", firstnumber + i);
        break;
      }
      pointparamlist[i].type = (int) strtol (stringptr, &stringptr, 0);
      if ((pointparamlist[i].type < 0) || (pointparamlist[i].type > 2)) {
        printf("Error:  Point %d has an invalid type.\n", firstnumber + i);
        break;
      }
    }
  }
  if (i < numberofpoints) {
    // Failed to read points due to some error.
    delete [] pointlist;
    pointlist = (REAL *) nullptr;
    if (markers) {
      delete [] pointmarkerlist;
      pointmarkerlist = (int *) nullptr;
    }
    if (numberofpointattributes > 0) {
      delete [] pointattributelist;
      pointattributelist = (REAL *) nullptr;
    }
    if (uvflag) {
      delete [] pointparamlist;
      pointparamlist = nullptr;
    }
    numberofpoints = 0;
    return false;
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node()    Load a list of points from a .node file.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_node(char* filebasename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  bool okflag;
  int markers;
  int uvflag; // for psc input.

  // Assembling the actual file names we want to open.
  strcpy(innodefilename, filebasename);
  strcat(innodefilename, ".node");

  // Try to open a .node file.
  infile = fopen(innodefilename, "r");
  if (infile == (FILE *) nullptr) {
    printf("  Cannot access file %s.\n", innodefilename);
    return false;
  }
  printf("Opening %s.\n", innodefilename); 

  // Set initial flags.
  mesh_dim = 3;
  numberofpointattributes = 0;  // no point attribute.
  markers = 0;  // no boundary marker.
  uvflag = 0; // no uv parameters (reuqired by a PSC). 

  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, innodefilename);
  // Does this file contain an index colume?
  stringptr = strstr(inputline, "rbox");
  if (stringptr == nullptr) {
    // Read number of points, number of dimensions, number of point
    //   attributes, and number of boundary markers. 
    stringptr = inputline;
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      uvflag = (int) strtol (stringptr, &stringptr, 0);
    }
  } else {
    // It is a rbox (qhull) input file.
    stringptr = inputline;
    // Get the dimension.
    mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    // Get the number of points.
    stringptr = readnumberline(inputline, infile, innodefilename);
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    // There is no index column.
    useindex = 0;
  }

  // Load the list of nodes.
  okflag = load_node_call(infile, markers, uvflag, innodefilename);

  fclose(infile);
  return okflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_edge()    Load a list of edges from a .edge file.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_edge(char* filebasename)
{
  FILE *infile;
  char inedgefilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  int markers, corner;
  int index;
  int i, j;

  strcpy(inedgefilename, filebasename);
  strcat(inedgefilename, ".edge");

  infile = fopen(inedgefilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", inedgefilename);
  } else {
    //printf("  Cannot access file %s.\n", inedgefilename);
    return false;
  }

  // Read number of boundary edges.
  stringptr = readnumberline(inputline, infile, inedgefilename);
  numberofedges = (int) strtol (stringptr, &stringptr, 0);
  if (numberofedges > 0) {
    edgelist = new int[numberofedges * 2];
    if (edgelist == (int *) nullptr) {
      terminatetetgen(1);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;  // Default value.
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
    if (markers > 0) {
      edgemarkerlist = new int[numberofedges];
    }
  }

  // Read the list of edges.
  index = 0;
  for (i = 0; i < numberofedges; i++) {
    // Read edge index and the edge's two endpoints.
    stringptr = readnumberline(inputline, infile, inedgefilename);
    for (j = 0; j < 2; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Edge %d is missing vertex %d in %s.\n",
               i + firstnumber, j + 1, inedgefilename);
        terminatetetgen(1);
      }
      corner = (int) strtol(stringptr, &stringptr, 0);
      if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
        printf("Error:  Edge %d has an invalid vertex index.\n",
               i + firstnumber);
        terminatetetgen(1);
      }
      edgelist[index++] = corner;
    }
    if (numberofcorners == 10) {
      // Skip an extra vertex (generated by a previous -o2 option).
      stringptr = findnextnumber(stringptr);
    }
    // Read the edge marker if it has.
    if (markers) {
      stringptr = findnextnumber(stringptr);
      edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0);
    }
  }

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_face()    Load a list of faces (triangles) from a .face file.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_face(char* filebasename)
{
  FILE *infile;
  char infilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL attrib;
  int markers, corner;
  int index;
  int i, j;

  strcpy(infilename, filebasename);
  strcat(infilename, ".face");

  infile = fopen(infilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", infilename);
  } else {
    return false;
  }

  // Read number of faces, boundary markers.
  stringptr = readnumberline(inputline, infile, infilename);
  numberoftrifaces = (int) strtol (stringptr, &stringptr, 0);
  stringptr = findnextnumber(stringptr);
  if (mesh_dim == 2) {
    // Skip a number.
    stringptr = findnextnumber(stringptr);
  }
  if (*stringptr == '\0') {
    markers = 0;  // Default there is no marker per face.
  } else {
    markers = (int) strtol (stringptr, &stringptr, 0);
  }
  if (numberoftrifaces > 0) {
    trifacelist = new int[numberoftrifaces * 3];
    if (trifacelist == (int *) nullptr) {
      terminatetetgen(1);
    }
    if (markers) {
      trifacemarkerlist = new int[numberoftrifaces];
      if (trifacemarkerlist == (int *) nullptr) {
        terminatetetgen(1);
      }
    }
  }

  // Read the list of faces.
  index = 0;
  for (i = 0; i < numberoftrifaces; i++) {
    // Read face index and the face's three corners.
    stringptr = readnumberline(inputline, infile, infilename);
    for (j = 0; j < 3; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Face %d is missing vertex %d in %s.\n",
               i + firstnumber, j + 1, infilename);
        terminatetetgen(1);
      }
      corner = (int) strtol(stringptr, &stringptr, 0);
      if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
        printf("Error:  Face %d has an invalid vertex index.\n",
               i + firstnumber);
        terminatetetgen(1);
      }
      trifacelist[index++] = corner;
    }
    if (numberofcorners == 10) {
      // Skip 3 extra vertices (generated by a previous -o2 option).
      for (j = 0; j < 3; j++) {
        stringptr = findnextnumber(stringptr);
      }
    }
    // Read the boundary marker if it exists.
    if (markers) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        attrib = 0.0;
      } else {
        attrib = (REAL) strtod(stringptr, &stringptr);
      }
      trifacemarkerlist[i] = (int) attrib;
    }
  }

  fclose(infile);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_tet()    Load a list of tetrahedra from a .ele file.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_tet(char* filebasename)
{
  FILE *infile;
  char infilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL attrib;
  int corner;
  int index, attribindex;
  int i, j;

  strcpy(infilename, filebasename);
  strcat(infilename, ".ele");

  infile = fopen(infilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", infilename);
  } else {
    return false;
  }

  // Read number of elements, number of corners (4 or 10), number of
  //   element attributes.
  stringptr = readnumberline(inputline, infile, infilename);
  numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0);
  if (numberoftetrahedra <= 0) {
    printf("Error:  Invalid number of tetrahedra.\n");
    fclose(infile);
    return false;
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr == '\0') {
    numberofcorners = 4;  // Default read 4 nodes per element.
  } else {
    numberofcorners = (int) strtol(stringptr, &stringptr, 0);
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr == '\0') {
    numberoftetrahedronattributes = 0; // Default no attribute.
  } else {
    numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0);
  }
  if (numberofcorners != 4 && numberofcorners != 10) {
    printf("Error:  Wrong number of corners %d (should be 4 or 10).\n", 
           numberofcorners);
    fclose(infile);
    return false;
  }

  // Allocate memory for tetrahedra.
  tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; 
  if (tetrahedronlist == (int *) nullptr) {
    terminatetetgen(1);
  }
  // Allocate memory for output tetrahedron attributes if necessary.
  if (numberoftetrahedronattributes > 0) {
    tetrahedronattributelist = new REAL[numberoftetrahedra *
                                        numberoftetrahedronattributes];
    if (tetrahedronattributelist == (REAL *) nullptr) {
      terminatetetgen(1);
    }
  }

  // Read the list of tetrahedra.
  index = 0;
  attribindex = 0;
  for (i = 0; i < numberoftetrahedra; i++) {
    // Read tetrahedron index and the tetrahedron's corners.
    stringptr = readnumberline(inputline, infile, infilename);
    for (j = 0; j < numberofcorners; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Tetrahedron %d is missing vertex %d in %s.\n",
               i + firstnumber, j + 1, infilename);
        terminatetetgen(1);
      }
      corner = (int) strtol(stringptr, &stringptr, 0);
      if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
        printf("Error:  Tetrahedron %d has an invalid vertex index.\n",
               i + firstnumber);
        terminatetetgen(1);
      }
      tetrahedronlist[index++] = corner;
    }
    // Read the tetrahedron's attributes.
    for (j = 0; j < numberoftetrahedronattributes; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        attrib = 0.0;
      } else {
        attrib = (REAL) strtod(stringptr, &stringptr);
      }
      tetrahedronattributelist[attribindex++] = attrib;
    }
  }

  fclose(infile);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_vol()    Load a list of volume constraints from a .vol file.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_vol(char* filebasename)
{
  FILE *infile;
  char inelefilename[FILENAMESIZE];
  char infilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr; 
  REAL volume;
  int volelements;
  int i;

  strcpy(infilename, filebasename);
  strcat(infilename, ".vol");

  infile = fopen(infilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", infilename);
  } else {
    return false;
  }

  // Read number of tetrahedra.
  stringptr = readnumberline(inputline, infile, infilename);
  volelements = (int) strtol (stringptr, &stringptr, 0);
  if (volelements != numberoftetrahedra) {
    strcpy(inelefilename, filebasename);
    strcat(infilename, ".ele");
    printf("Warning:  %s and %s disagree on number of tetrahedra.\n",
           inelefilename, infilename);
    fclose(infile);
    return false;
  }

  tetrahedronvolumelist = new REAL[volelements];
  if (tetrahedronvolumelist == (REAL *) nullptr) {
    terminatetetgen(1);
  }

  // Read the list of volume constraints.
  for (i = 0; i < volelements; i++) {
    stringptr = readnumberline(inputline, infile, infilename);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      volume = -1.0; // No constraint on this tetrahedron.
    } else {
      volume = (REAL) strtod(stringptr, &stringptr);
    }
    tetrahedronvolumelist[i] = volume;
  }

  fclose(infile);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_var()    Load constraints applied on facets, segments, and nodes     //
//               from a .var file.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_var(char* filebasename)
{
  FILE *infile;
  char varfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  int index;
  int i;

  // Variant constraints are saved in file "filename.var".
  strcpy(varfilename, filebasename);
  strcat(varfilename, ".var");
  infile = fopen(varfilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", varfilename);
  } else {
    return false;
  }

  // Read the facet constraint section.
  stringptr = readnumberline(inputline, infile, varfilename);
  if (*stringptr != '\0') {
    numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0);
  } else {
    numberoffacetconstraints = 0;
  }
  if (numberoffacetconstraints > 0) {
    // Initialize 'facetconstraintlist'.
    facetconstraintlist = new REAL[numberoffacetconstraints * 2];
    index = 0;
    for (i = 0; i < numberoffacetconstraints; i++) {
      stringptr = readnumberline(inputline, infile, varfilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  facet constraint %d has no facet marker.\n",
               firstnumber + i);
        break;
      } else {
        facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  facet constraint %d has no maximum area bound.\n",
               firstnumber + i);
        break;
      } else {
        facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
    }
    if (i < numberoffacetconstraints) {
      // This must be caused by an error.
      fclose(infile);
      return false;
    }
  }

  // Read the segment constraint section.
  stringptr = readnumberline(inputline, infile, varfilename);
  if (*stringptr != '\0') {
    numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0);
  } else {
    numberofsegmentconstraints = 0;
  }
  if (numberofsegmentconstraints > 0) {
    // Initialize 'segmentconstraintlist'.
    segmentconstraintlist = new REAL[numberofsegmentconstraints * 3];
    index = 0;
    for (i = 0; i < numberofsegmentconstraints; i++) {
      stringptr = readnumberline(inputline, infile, varfilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  segment constraint %d has no frist endpoint.\n",
               firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  segment constraint %d has no second endpoint.\n",
               firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  segment constraint %d has no maximum length bound.\n",
               firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
    }
    if (i < numberofsegmentconstraints) {
      // This must be caused by an error.
      fclose(infile);
      return false;
    }
  }

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_mtr()    Load a size specification map from a .mtr file.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_mtr(char* filebasename)
{
  FILE *infile;
  char mtrfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL mtr;
  int ptnum;
  int mtrindex;
  int i, j;

  strcpy(mtrfilename, filebasename);
  strcat(mtrfilename, ".mtr");
  infile = fopen(mtrfilename, "r");
  if (infile != (FILE *) nullptr) {
    printf("Opening %s.\n", mtrfilename);
  } else {
    return false;
  }

  // Read the number of points.
  stringptr = readnumberline(inputline, infile, mtrfilename);
  ptnum = (int) strtol (stringptr, &stringptr, 0);
  if (ptnum != numberofpoints) {
    printf("  !! Point numbers are not equal. Ignored.\n");
    fclose(infile);
    return false;
  }
  // Read the number of columns (1, 3, or 6).
  stringptr = findnextnumber(stringptr); // Skip number of points.
  if (*stringptr != '\0') {
    numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0);
  }
  if (numberofpointmtrs == 0) {
    // Column number doesn't match. Set a default number (1).
    numberofpointmtrs = 1;
  }

  // Allocate space for pointmtrlist.
  pointmtrlist = new REAL[numberofpoints * numberofpointmtrs];
  if (pointmtrlist == (REAL *) nullptr) {
    terminatetetgen(1);
  }
  mtrindex = 0;
  for (i = 0; i < numberofpoints; i++) {
    // Read metrics.
    stringptr = readnumberline(inputline, infile, mtrfilename);
    for (j = 0; j < numberofpointmtrs; j++) {
      if (*stringptr == '\0') {
        printf("Error:  Metric %d is missing value #%d in %s.\n",
               i + firstnumber, j + 1, mtrfilename);
        terminatetetgen(1);
      }
      mtr = (REAL) strtod(stringptr, &stringptr);
      pointmtrlist[mtrindex++] = mtr;
      stringptr = findnextnumber(stringptr);
    }
  }

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_poly()    Load a PL complex from a .poly or a .smesh file.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_poly(char* filebasename)
{
  FILE *infile;
  char inpolyfilename[FILENAMESIZE];
  char insmeshfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr, *infilename;
  int smesh, markers, uvflag, currentmarker;
  int index;
  int i, j, k;

  // Assembling the actual file names we want to open.
  strcpy(inpolyfilename, filebasename);
  strcpy(insmeshfilename, filebasename);
  strcat(inpolyfilename, ".poly");
  strcat(insmeshfilename, ".smesh");

  // First assume it is a .poly file.
  smesh = 0;
  // Try to open a .poly file.
  infile = fopen(inpolyfilename, "r");
  if (infile == (FILE *) nullptr) {
    // .poly doesn't exist! Try to open a .smesh file.
    infile = fopen(insmeshfilename, "r");
    if (infile == (FILE *) nullptr) {
      printf("  Cannot access file %s and %s.\n",
             inpolyfilename, insmeshfilename);
      return false;
    } else {
      printf("Opening %s.\n", insmeshfilename);
      infilename = insmeshfilename;
    }
    smesh = 1;
  } else {
    printf("Opening %s.\n", inpolyfilename);
    infilename = inpolyfilename;
  }

  // Initialize the default values.
  mesh_dim = 3;  // Three-dimemsional accoordinates.
  numberofpointattributes = 0;  // no point attribute.
  markers = 0;  // no boundary marker.
  uvflag = 0; // no uv parameters (reuqired by a PSC).

  // Read number of points, number of dimensions, number of point
  //   attributes, and number of boundary markers.
  stringptr = readnumberline(inputline, infile, infilename);
  numberofpoints = (int) strtol (stringptr, &stringptr, 0);
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    mesh_dim = (int) strtol (stringptr, &stringptr, 0);      
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    markers = (int) strtol (stringptr, &stringptr, 0);
  }
  if (*stringptr != '\0') {
    uvflag = (int) strtol (stringptr, &stringptr, 0);
  }

  if (numberofpoints > 0) {
    // Load the list of nodes.
    if (!load_node_call(infile, markers, uvflag, infilename)) {
      fclose(infile);
      return false;
    }
  } else {
    // If the .poly or .smesh file claims there are zero points, that
    //   means the points should be read from a separate .node file.
    if (!load_node(filebasename)) {
      fclose(infile);
      return false;
    }
  }

  if ((mesh_dim != 3) && (mesh_dim != 2)) {
    printf("Input error:  TetGen only works for 2D & 3D point sets.\n");
    fclose(infile);
    return false;
  }
  if (numberofpoints < (mesh_dim + 1)) {
    printf("Input error:  TetGen needs at least %d points.\n", mesh_dim + 1);
    fclose(infile);
    return false;
  }

  facet *f;
  polygon *p;

  if (mesh_dim == 3) {

    // Read number of facets and number of boundary markers.
    stringptr = readnumberline(inputline, infile, infilename);
    if (stringptr == nullptr) {
      // No facet list, return.
      fclose(infile);
      return true;
    }
    numberoffacets = (int) strtol (stringptr, &stringptr, 0);
    if (numberoffacets <= 0) {
      // No facet list, return.
      fclose(infile);
      return true;
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;  // no boundary marker.
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }

    // Initialize the 'facetlist', 'facetmarkerlist'.
    facetlist = new facet[numberoffacets];
    if (markers == 1) {
      facetmarkerlist = new int[numberoffacets];
    }

    // Read data into 'facetlist', 'facetmarkerlist'.
    if (smesh == 0) {
      // Facets are in .poly file format.
      for (i = 1; i <= numberoffacets; i++) {
        f = &(facetlist[i - 1]);
        init(f);
        f->numberofholes = 0;
        currentmarker = 0;
        // Read number of polygons, number of holes, and a boundary marker.
        stringptr = readnumberline(inputline, infile, infilename);
        f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
        stringptr = findnextnumber(stringptr);
        if (*stringptr != '\0') {
          f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
          if (markers == 1) {
            stringptr = findnextnumber(stringptr);
            if (*stringptr != '\0') {
              currentmarker = (int) strtol(stringptr, &stringptr, 0);
            }
          }
        }
        // Initialize facetmarker if it needs.
        if (markers == 1) {
          facetmarkerlist[i - 1] = currentmarker; 
        }
        // Each facet should has at least one polygon.
        if (f->numberofpolygons <= 0) {
          printf("Error:  Wrong number of polygon in %d facet.\n", i);
          break; 
        }
        // Initialize the 'f->polygonlist'.
        f->polygonlist = new polygon[f->numberofpolygons];
        // Go through all polygons, read in their vertices.
        for (j = 1; j <= f->numberofpolygons; j++) {
          p = &(f->polygonlist[j - 1]);
          init(p);
          // Read number of vertices of this polygon.
          stringptr = readnumberline(inputline, infile, infilename);
          p->numberofvertices = (int) strtol(stringptr, &stringptr, 0);
          if (p->numberofvertices < 1) {
            printf("Error:  Wrong polygon %d in facet %d\n", j, i);
            break;
          }
          // Initialize 'p->vertexlist'.
          p->vertexlist = new int[p->numberofvertices];
          // Read all vertices of this polygon.
          for (k = 1; k <= p->numberofvertices; k++) {
            stringptr = findnextnumber(stringptr);
            if (*stringptr == '\0') {
              // Try to load another non-empty line and continue to read the
              //   rest of vertices.
              stringptr = readnumberline(inputline, infile, infilename);
              if (*stringptr == '\0') {
                printf("Error: Missing %d endpoints of polygon %d in facet %d",
                       p->numberofvertices - k, j, i);
                break;
              }
            }
            p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
          }
        } 
        if (j <= f->numberofpolygons) {
          // This must be caused by an error. However, there're j - 1
          //   polygons have been read. Reset the 'f->numberofpolygon'.
          if (j == 1) {
            // This is the first polygon.
            delete [] f->polygonlist;
          }
          f->numberofpolygons = j - 1;
          // No hole will be read even it exists.
          f->numberofholes = 0;
          break;
        }
        // If this facet has hole pints defined, read them.
        if (f->numberofholes > 0) {
          // Initialize 'f->holelist'.
          f->holelist = new REAL[f->numberofholes * 3];
          // Read the holes' coordinates.
          index = 0;
          for (j = 1; j <= f->numberofholes; j++) {
            stringptr = readnumberline(inputline, infile, infilename);
            for (k = 1; k <= 3; k++) {
              stringptr = findnextnumber(stringptr);
              if (*stringptr == '\0') {
                printf("Error:  Hole %d in facet %d has no coordinates", j, i);
                break;
              }
              f->holelist[index++] = (REAL) strtod (stringptr, &stringptr);
            }
            if (k <= 3) {
              // This must be caused by an error.
              break;
            }
          }
          if (j <= f->numberofholes) {
            // This must be caused by an error.
            break;
          }
        }
      }
      if (i <= numberoffacets) {
        // This must be caused by an error.
        numberoffacets = i - 1;
        fclose(infile);
        return false;
      }
    } else { // poly == 0
      // Read the facets from a .smesh file.
      for (i = 1; i <= numberoffacets; i++) {
        f = &(facetlist[i - 1]);
        init(f);
        // Initialize 'f->facetlist'. In a .smesh file, each facetlist only
        //   contains exactly one polygon, no hole.
        f->numberofpolygons = 1;
        f->polygonlist = new polygon[f->numberofpolygons];
        p = &(f->polygonlist[0]);
        init(p);
        // Read number of vertices of this polygon.
        stringptr = readnumberline(inputline, infile, insmeshfilename);
        p->numberofvertices = (int) strtol (stringptr, &stringptr, 0);
        if (p->numberofvertices < 1) {
          printf("Error:  Wrong number of vertex in facet %d\n", i);
          break;
        }
        // Initialize 'p->vertexlist'.
        p->vertexlist = new int[p->numberofvertices];
        for (k = 1; k <= p->numberofvertices; k++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            // Try to load another non-empty line and continue to read the
            //   rest of vertices.
            stringptr = readnumberline(inputline, infile, infilename);
            if (*stringptr == '\0') {
              printf("Error:  Missing %d endpoints in facet %d",
                     p->numberofvertices - k, i);
              break;
            }
          }
          p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
        }
        if (k <= p->numberofvertices) {
          // This must be caused by an error.
          break;
        }
        // Read facet's boundary marker at last.
        if (markers == 1) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            currentmarker = 0;
          } else {
            currentmarker = (int) strtol(stringptr, &stringptr, 0);
          }
          facetmarkerlist[i - 1] = currentmarker;
        }
      }
      if (i <= numberoffacets) {
        // This must be caused by an error.
        numberoffacets = i - 1;
        fclose(infile);
        return false;
      }
    }

    // Read the hole section.
    stringptr = readnumberline(inputline, infile, infilename);
    if (stringptr == nullptr) {
      // No hole list, return.
      fclose(infile);
      return true;
    }
    if (*stringptr != '\0') {
      numberofholes = (int) strtol (stringptr, &stringptr, 0);
    } else {
      numberofholes = 0;
    }
    if (numberofholes > 0) {
      // Initialize 'holelist'.
      holelist = new REAL[numberofholes * 3];
      for (i = 0; i < 3 * numberofholes; i += 3) {
        stringptr = readnumberline(inputline, infile, infilename);
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Hole %d has no x coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Hole %d has no y coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Hole %d has no z coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i + 2] = (REAL) strtod(stringptr, &stringptr);
        }
      }
      if (i < 3 * numberofholes) {
        // This must be caused by an error.
        fclose(infile);
        return false;
      }
    }

    // Read the region section.  The 'region' section is optional, if we
    //   don't reach the end-of-file, try read it in.
    stringptr = readnumberline(inputline, infile, nullptr);
    if (stringptr != (char *) nullptr && *stringptr != '\0') {
      numberofregions = (int) strtol (stringptr, &stringptr, 0);
    } else {
      numberofregions = 0;
    }
    if (numberofregions > 0) {
      // Initialize 'regionlist'.
      regionlist = new REAL[numberofregions * 5];
      index = 0;
      for (i = 0; i < numberofregions; i++) {
        stringptr = readnumberline(inputline, infile, infilename);
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Region %d has no x coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Region %d has no y coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Region %d has no z coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Region %d has no region attrib.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          regionlist[index] = regionlist[index - 1];
        } else {
          regionlist[index] = (REAL) strtod(stringptr, &stringptr);
        }
        index++;
      }
      if (i < numberofregions) {
        // This must be caused by an error.
        fclose(infile);
        return false;
      }
    }

  } else {

    // Read a PSLG from Triangle's poly file.
    assert(mesh_dim == 2);
    // A PSLG is a facet of a PLC.
    numberoffacets = 1;
    // Initialize the 'facetlist'.
    facetlist = new facet[numberoffacets];
    facetmarkerlist = (int *) nullptr; // No facet markers.
    f = &(facetlist[0]);
    init(f);
    // Read number of segments.
    stringptr = readnumberline(inputline, infile, infilename);
    // Segments are degenerate polygons.
    f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
    if (f->numberofpolygons > 0) {
      f->polygonlist = new polygon[f->numberofpolygons];
    }
    // Go through all segments, read in their vertices.
    for (j = 0; j < f->numberofpolygons; j++) {
      p = &(f->polygonlist[j]);
      init(p);
      // Read in a segment.
      stringptr = readnumberline(inputline, infile, infilename);
      stringptr = findnextnumber(stringptr); // Skip its index.
      p->numberofvertices = 2; // A segment always has two vertices.
      p->vertexlist = new int[p->numberofvertices];
      p->vertexlist[0] = (int) strtol (stringptr, &stringptr, 0);
      stringptr = findnextnumber(stringptr);
      p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0);
    }
    // Read number of holes.
    stringptr = readnumberline(inputline, infile, infilename);
    f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
    if (f->numberofholes > 0) {
      // Initialize 'f->holelist'.
      f->holelist = new REAL[f->numberofholes * 3];
      // Read the holes' coordinates.
      for (j = 0; j < f->numberofholes; j++) {
        // Read a 2D hole point.
        stringptr = readnumberline(inputline, infile, infilename);
        stringptr = findnextnumber(stringptr); // Skip its index.
        f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr);
        stringptr = findnextnumber(stringptr);
        f->holelist[j * 3 + 1] = (REAL) strtod (stringptr, &stringptr);
        f->holelist[j * 3 + 2] = 0.0; // The z-coord.
      }
    }
    // The regions are skipped.

  }

  // End of reading poly/smesh file.
  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_off()    Load a polyhedron from a .off file.                         //
//                                                                           //
// The .off format is one of file formats of the Geomview, an interactive    //
// program for viewing and manipulating geometric objects.  More information //
// is available form: http://www.geomview.org.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_off(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp;
  double *coord;
  int nverts = 0, iverts = 0;
  int nfaces = 0, ifaces = 0;
  int nedges = 0;
  int line_count = 0, i;

  // Default, the off file's index is from '0'. We check it by remembering the
  //   smallest index we found in the file. It should be either 0 or 1.
  int smallestidx = 0; 

  strncpy(infilename, filebasename, 1024 - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) {
    strcat(infilename, ".off");
  }

  if (!(fp = fopen(infilename, "r"))) {
    printf("  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  while ((bufferp = readline(buffer, fp, &line_count)) != nullptr) {
    // Check section
    if (nverts == 0) {
      // Read header 
      bufferp = strstr(bufferp, "OFF");
      if (bufferp != nullptr) {
        // Read mesh counts
        bufferp = findnextnumber(bufferp); // Skip field "OFF".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) 
            || (nverts == 0)) {
          printf("Syntax error reading header on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        // Allocate memory for 'tetgenio'
        if (nverts > 0) {
          numberofpoints = nverts;
          pointlist = new REAL[nverts * 3];
          smallestidx = nverts + 1; // A bigger enough number.
        }
        if (nfaces > 0) {        
          numberoffacets = nfaces;
          facetlist = new tetgenio::facet[nfaces];
        }
      }
    } else if (iverts < nverts) {
      // Read vertex coordinates
      coord = &pointlist[iverts * 3];
      for (i = 0; i < 3; i++) {
        if (*bufferp == '\0') {
          printf("Syntax error reading vertex coords on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        coord[i] = (REAL) strtod(bufferp, &bufferp);
        bufferp = findnextnumber(bufferp);
      }
      iverts++;
    } else if (ifaces < nfaces) {
      // Get next face
      f = &facetlist[ifaces];
      init(f);      
      // In .off format, each facet has one polygon, no hole.
      f->numberofpolygons = 1;
      f->polygonlist = new tetgenio::polygon[1];
      p = &f->polygonlist[0];
      init(p);
      // Read the number of vertices, it should be greater than 0.
      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
      if (p->numberofvertices == 0) {
        printf("Syntax error reading polygon on line %d in file %s\n",
               line_count, infilename);
        fclose(fp);
        return false;
      }
      // Allocate memory for face vertices
      p->vertexlist = new int[p->numberofvertices];
      for (i = 0; i < p->numberofvertices; i++) {
        bufferp = findnextnumber(bufferp);
        if (*bufferp == '\0') {
          printf("Syntax error reading polygon on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
        // Detect the smallest index.
        if (p->vertexlist[i] < smallestidx) {
          smallestidx = p->vertexlist[i];
        }
      }
      ifaces++;
    } else {
      // Should never get here
      printf("Found extra text starting at line %d in file %s\n", line_count,
             infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Decide the firstnumber of the index.
  if (smallestidx == 0) {
    firstnumber = 0;  
  } else if (smallestidx == 1) {
    firstnumber = 1;
  } else {
    printf("A wrong smallest index (%d) was detected in file %s\n",
           smallestidx, infilename);
    return false;
  }

  if (iverts != nverts) {
    printf("Expected %d vertices, but read only %d vertices in file %s\n",
           nverts, iverts, infilename);
    return false;
  }
  if (ifaces != nfaces) {
    printf("Expected %d faces, but read only %d faces in file %s\n",
           nfaces, ifaces, infilename);
    return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_ply()    Load a polyhedron from a .ply file.                         //
//                                                                           //
// This is a simplified version of reading .ply files, which only reads the  //
// set of vertices and the set of faces. Other informations (such as color,  //
// material, texture, etc) in .ply file are ignored. Complete routines for   //
// reading and writing ,ply files are available from: http://www.cc.gatech.  //
// edu/projects/large_models/ply.html.  Except the header section, ply file  //
// format has exactly the same format for listing vertices and polygons as   //
// off file format.                                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_ply(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int endheader = 0, format = 0;
  int nverts = 0, iverts = 0;
  int nfaces = 0, ifaces = 0;
  int line_count = 0, i;

  // Default, the ply file's index is from '0'. We check it by remembering the
  //   smallest index we found in the file. It should be either 0 or 1.
  int smallestidx = 0; 

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) {
    strcat(infilename, ".ply");
  }

  if (!(fp = fopen(infilename, "r"))) {
    printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  while ((bufferp = readline(buffer, fp, &line_count)) != nullptr) {
    if (!endheader) {
      // Find if it is the keyword "end_header".
      str = strstr(bufferp, "end_header");
      // strstr() is case sensitive.
      if (!str) str = strstr(bufferp, "End_header");
      if (!str) str = strstr(bufferp, "End_Header");
      if (str) {
        // This is the end of the header section.
        endheader = 1; 
        continue;
      }
      // Parse the number of vertices and the number of faces.
      if (nverts == 0 || nfaces == 0) {
        // Find if it si the keyword "element".
        str = strstr(bufferp, "element");
        if (!str) str = strstr(bufferp, "Element");
        if (str) {
          bufferp = findnextfield(str);
          if (*bufferp == '\0') {
            printf("Syntax error reading element type on line%d in file %s\n",
                   line_count, infilename);
            fclose(fp);
            return false;
          }
          if (nverts == 0) {
            // Find if it is the keyword "vertex".
            str = strstr(bufferp, "vertex");
            if (!str) str = strstr(bufferp, "Vertex");
            if (str) {
              bufferp = findnextnumber(str);
              if (*bufferp == '\0') {
                printf("Syntax error reading vertex number on line");
                printf(" %d in file %s\n", line_count, infilename);
                fclose(fp);
                return false;
              }
              nverts = (int) strtol(bufferp, &bufferp, 0);
              // Allocate memory for 'tetgenio'
              if (nverts > 0) {
                numberofpoints = nverts;
                pointlist = new REAL[nverts * 3];
                smallestidx = nverts + 1; // A big enough index.
              }
            }
          }
          if (nfaces == 0) {
            // Find if it is the keyword "face".
            str = strstr(bufferp, "face");
            if (!str) str = strstr(bufferp, "Face");
            if (str) {
              bufferp = findnextnumber(str);
              if (*bufferp == '\0') {
                printf("Syntax error reading face number on line");
                printf(" %d in file %s\n", line_count, infilename);
                fclose(fp);
                return false;
              }
              nfaces = (int) strtol(bufferp, &bufferp, 0);
              // Allocate memory for 'tetgenio'
              if (nfaces > 0) {        
                numberoffacets = nfaces;
                facetlist = new tetgenio::facet[nfaces];
              }
            }
          }
        } // It is not the string "element". 
      }
      if (format == 0) {
        // Find the keyword "format".
        str = strstr(bufferp, "format");
        if (!str) str = strstr(bufferp, "Format");
        if (str) {
          format = 1;
          bufferp = findnextfield(str);
          // Find if it is the string "ascii".
          str = strstr(bufferp, "ascii");
          if (!str) str = strstr(bufferp, "ASCII");
          if (!str) {
            printf("This routine only reads ascii format of ply files.\n");
            printf("Hint: You can convert the binary to ascii format by\n");
            printf("  using the provided ply tools:\n");
            printf("  ply2ascii < %s > ascii_%s\n", infilename, infilename);
            fclose(fp);
            return false;
          }
        }
      }
    } else if (iverts < nverts) {
      // Read vertex coordinates
      coord = &pointlist[iverts * 3];
      for (i = 0; i < 3; i++) {
        if (*bufferp == '\0') {
          printf("Syntax error reading vertex coords on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        coord[i] = (REAL) strtod(bufferp, &bufferp);
        bufferp = findnextnumber(bufferp);
      }
      iverts++;
    } else if (ifaces < nfaces) {
      // Get next face
      f = &facetlist[ifaces];
      init(f);      
      // In .off format, each facet has one polygon, no hole.
      f->numberofpolygons = 1;
      f->polygonlist = new tetgenio::polygon[1];
      p = &f->polygonlist[0];
      init(p);
      // Read the number of vertices, it should be greater than 0.
      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
      if (p->numberofvertices == 0) {
        printf("Syntax error reading polygon on line %d in file %s\n",
               line_count, infilename);
        fclose(fp);
        return false;
      }
      // Allocate memory for face vertices
      p->vertexlist = new int[p->numberofvertices];
      for (i = 0; i < p->numberofvertices; i++) {
        bufferp = findnextnumber(bufferp);
        if (*bufferp == '\0') {
          printf("Syntax error reading polygon on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
        if (p->vertexlist[i] < smallestidx) {
          smallestidx = p->vertexlist[i];
        }
      }
      ifaces++;
    } else {
      // Should never get here
      printf("Found extra text starting at line %d in file %s\n", line_count,
             infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Decide the firstnumber of the index.
  if (smallestidx == 0) {
    firstnumber = 0;  
  } else if (smallestidx == 1) {
    firstnumber = 1;
  } else {
    printf("A wrong smallest index (%d) was detected in file %s\n",
           smallestidx, infilename);
    return false;
  }

  if (iverts != nverts) {
    printf("Expected %d vertices, but read only %d vertices in file %s\n",
           nverts, iverts, infilename);
    return false;
  }
  if (ifaces != nfaces) {
    printf("Expected %d faces, but read only %d faces in file %s\n",
           nfaces, ifaces, infilename);
    return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_stl()    Load a surface mesh from a .stl file.                       //
//                                                                           //
// The .stl or stereolithography format is an ASCII or binary file used in   //
// manufacturing.  It is a list of the triangular surfaces that describe a   //
// computer generated solid model. This is the standard input for most rapid //
// prototyping machines.                                                     //
//                                                                           //
// Comment: A .stl file many contain many duplicated points.  They will be   //
// unified during the Delaunay tetrahedralization process.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_stl(char* filebasename)
{
  FILE *fp;
  tetgenmesh::arraypool *plist;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int solid = 0;
  int nverts = 0, iverts = 0;
  int nfaces = 0;
  int line_count = 0, i;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) {
    strcat(infilename, ".stl");
  }

  if (!(fp = fopen(infilename, "r"))) {
    printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  // STL file has no number of points available. Use a list to read points.
  plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10); 

  while ((bufferp = readline(buffer, fp, &line_count)) != nullptr) {
    // The ASCII .stl file must start with the lower case keyword solid and
    //   end with endsolid.
    if (solid == 0) {
      // Read header 
      bufferp = strstr(bufferp, "solid");
      if (bufferp != nullptr) {
        solid = 1;
      }
    } else {
      // We're inside the block of the solid.
      str = bufferp;
      // Is this the end of the solid.
      bufferp = strstr(bufferp, "endsolid");
      if (bufferp != nullptr) {
        solid = 0;
      } else {
        // Read the XYZ coordinates if it is a vertex.
        bufferp = str;
        bufferp = strstr(bufferp, "vertex");
        if (bufferp != nullptr) {
          plist->newindex((void **) &coord);
          for (i = 0; i < 3; i++) {
            bufferp = findnextnumber(bufferp);
            if (*bufferp == '\0') {
              printf("Syntax error reading vertex coords on line %d\n",
                   line_count);
              delete plist;
              fclose(fp);
              return false;
            }
            coord[i] = (REAL) strtod(bufferp, &bufferp);
          }
        }
      }
    }
  }
  fclose(fp);

  nverts = (int) plist->objects;
  // nverts should be an integer times 3 (every 3 vertices denote a face).
  if (nverts == 0 || (nverts % 3 != 0)) {
    printf("Error:  Wrong number of vertices in file %s.\n", infilename);
    delete plist;
    return false;
  }
  numberofpoints = nverts;
  pointlist = new REAL[nverts * 3];
  for (i = 0; i < nverts; i++) {
    coord = (double *) fastlookup(plist, i);
    iverts = i * 3;
    pointlist[iverts] = (REAL) coord[0];
    pointlist[iverts + 1] = (REAL) coord[1];
    pointlist[iverts + 2] = (REAL) coord[2];
  }

  nfaces = (int) (nverts / 3);
  numberoffacets = nfaces;
  facetlist = new tetgenio::facet[nfaces];

  // Default use '1' as the array starting index.
  firstnumber = 1;
  iverts = firstnumber;
  for (i = 0; i < nfaces; i++) {
    f = &facetlist[i];
    init(f);      
    // In .stl format, each facet has one polygon, no hole.
    f->numberofpolygons = 1;
    f->polygonlist = new tetgenio::polygon[1];
    p = &f->polygonlist[0];
    init(p);
    // Each polygon has three vertices.
    p->numberofvertices = 3;
    p->vertexlist = new int[p->numberofvertices];
    p->vertexlist[0] = iverts;
    p->vertexlist[1] = iverts + 1;
    p->vertexlist[2] = iverts + 2;
    iverts += 3;
  }

  delete plist;
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_medit()    Load a surface mesh from a .mesh file.                    //
//                                                                           //
// The .mesh format is the file format of Medit, a user-friendly interactive //
// mesh viewer program.                                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_medit(char* filebasename, int istetmesh)
{
  FILE *fp;
  tetgenio::facet *tmpflist, *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int *tmpfmlist;
  int dimension = 0;
  int nverts = 0;
  int nfaces = 0;
  int ntets = 0;
  int line_count = 0;
  int corners = 0; // 3 (triangle) or 4 (quad).
  int *plist;
  int i, j;

  int smallestidx = 0;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) {
    strcat(infilename, ".mesh");
  }
  
  if (!(fp = fopen(infilename, "r"))) {
    printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  while ((bufferp = readline(buffer, fp, &line_count)) != nullptr) {
    if (*bufferp == '#') continue;  // A comment line is skipped.
    if (dimension == 0) {
      // Find if it is the keyword "Dimension".
      str = strstr(bufferp, "Dimension");
      if (!str) str = strstr(bufferp, "dimension");
      if (!str) str = strstr(bufferp, "DIMENSION");
      if (str) {
        // Read the dimensions
        bufferp = findnextnumber(str); // Skip field "Dimension".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        dimension = (int) strtol(bufferp, &bufferp, 0);
        if (dimension != 2 && dimension != 3) {
          printf("Unknown dimension in file on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        mesh_dim = dimension;
      }
    }
    if (nverts == 0) {
      // Find if it is the keyword "Vertices".
      str = strstr(bufferp, "Vertices");
      if (!str) str = strstr(bufferp, "vertices");
      if (!str) str = strstr(bufferp, "VERTICES");
      if (str) {
        // Read the number of vertices.
        bufferp = findnextnumber(str); // Skip field "Vertices".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        nverts = (int) strtol(bufferp, &bufferp, 0);
        // Initialize the smallest index.
        smallestidx = nverts + 1;
        // Allocate memory for 'tetgenio'
        if (nverts > 0) {
          numberofpoints = nverts;
          pointlist = new REAL[nverts * 3];
        }
        // Read the follwoing node list.
        for (i = 0; i < nverts; i++) {
          bufferp = readline(buffer, fp, &line_count);
          if (bufferp == nullptr) {
            printf("Unexpected end of file on line %d in file %s\n",
                   line_count, infilename);
            fclose(fp);
            return false;
          }
          // Read vertex coordinates
          coord = &pointlist[i * 3];
          for (j = 0; j < 3; j++) {
            if (*bufferp == '\0') {
              printf("Syntax error reading vertex coords on line");
              printf(" %d in file %s\n", line_count, infilename);
              fclose(fp);
              return false;
            }
            if ((j < 2) || (dimension == 3)) {
              coord[j] = (REAL) strtod(bufferp, &bufferp);
            } else {
              assert((j == 2) && (dimension == 2));
              coord[j] = 0.0;
            }
            bufferp = findnextnumber(bufferp);
          }
        }
        continue;
      }
    }
    if (ntets == 0) {
      // Find if it is the keyword "Tetrahedra"
      corners = 0;
      str = strstr(bufferp, "Tetrahedra");
      if (!str) str = strstr(bufferp, "tetrahedra");
      if (!str) str = strstr(bufferp, "TETRAHEDRA");
      if (str) {
        corners = 4;
      }
      if (corners == 4) {
        // Read the number of tetrahedra
        bufferp = findnextnumber(str); // Skip field "Tetrahedra".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        ntets = strtol(bufferp, &bufferp, 0);
        if (ntets > 0) {
          // It is a tetrahedral mesh.
          numberoftetrahedra = ntets;
          numberofcorners = 4;
          numberoftetrahedronattributes = 1;
          tetrahedronlist = new int[ntets * 4];
          tetrahedronattributelist = new REAL[ntets];
        }
      } // if (corners == 4)
      // Read the list of tetrahedra.
      for (i = 0; i < numberoftetrahedra; i++) {
        plist = &(tetrahedronlist[i * 4]);
        bufferp = readline(buffer, fp, &line_count);
        if (bufferp == nullptr) {
          printf("Unexpected end of file on line %d in file %s\n",
                 line_count, infilename);
          fclose(fp);
          return false;
        }
        // Read the vertices of the tet.
        for (j = 0; j < corners; j++) {
          if (*bufferp == '\0') {
            printf("Syntax error reading face on line %d in file %s\n",
                   line_count, infilename);
            fclose(fp);
            return false;
          }
          plist[j] = (int) strtol(bufferp, &bufferp, 0);
          // Remember the smallest index.
          if (plist[j] < smallestidx) smallestidx = plist[j];
          bufferp = findnextnumber(bufferp);
        }
        // Read the attribute of the tet if it exists.
        tetrahedronattributelist[i] = 0;
        if (*bufferp != '\0') {
          tetrahedronattributelist[i] = (REAL) strtol(bufferp, &bufferp, 0);
        }
      } // i
    } // Tetrahedra
    if (nfaces == 0) {
      // Find if it is the keyword "Triangles" or "Quadrilaterals".
      corners = 0;
      str = strstr(bufferp, "Triangles");
      if (!str) str = strstr(bufferp, "triangles");
      if (!str) str = strstr(bufferp, "TRIANGLES");
      if (str) {
        corners = 3;
      } else {
        str = strstr(bufferp, "Quadrilaterals");
        if (!str) str = strstr(bufferp, "quadrilaterals");
        if (!str) str = strstr(bufferp, "QUADRILATERALS");
        if (str) {
          corners = 4;
        }
      }
      if (corners == 3 || corners == 4) {
        // Read the number of triangles (or quadrilaterals).
        bufferp = findnextnumber(str); // Skip field "Triangles".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        nfaces = strtol(bufferp, &bufferp, 0);
        // Allocate memory for 'tetgenio'
        if (nfaces > 0) {
          if (!istetmesh) {
            // It is a PLC surface mesh.
            if (numberoffacets > 0) {
              // facetlist has already been allocated. Enlarge arrays.
              // This happens when the surface mesh contains mixed cells.
              tmpflist = new tetgenio::facet[numberoffacets + nfaces];
              tmpfmlist = new int[numberoffacets + nfaces];
              // Copy the data of old arrays into new arrays.
              for (i = 0; i < numberoffacets; i++) {
                f = &(tmpflist[i]);
                tetgenio::init(f);
                *f = facetlist[i];
                tmpfmlist[i] = facetmarkerlist[i];
              }
              // Release old arrays.
              delete [] facetlist;
              delete [] facetmarkerlist;
              // Remember the new arrays.
              facetlist = tmpflist;
              facetmarkerlist = tmpfmlist;
            } else {
              // This is the first time to allocate facetlist.
              facetlist = new tetgenio::facet[nfaces];
              facetmarkerlist = new int[nfaces];
            }
          } else {
            if (corners == 3) {
              // It is a surface mesh of a tetrahedral mesh.
              numberoftrifaces = nfaces;
              trifacelist = new int[nfaces * 3];
              trifacemarkerlist = new int[nfaces];
            }
          }
        } // if (nfaces > 0)
        // Read the following list of faces.
        if (!istetmesh) {
          for (i = numberoffacets; i < numberoffacets + nfaces; i++) {
            bufferp = readline(buffer, fp, &line_count);
            if (bufferp == nullptr) {
              printf("Unexpected end of file on line %d in file %s\n",
                     line_count, infilename);
              fclose(fp);
              return false;
            }
            f = &facetlist[i];
            tetgenio::init(f);
            // In .mesh format, each facet has one polygon, no hole.
            f->numberofpolygons = 1;
            f->polygonlist = new tetgenio::polygon[1];
            p = &f->polygonlist[0];
            tetgenio::init(p);
            p->numberofvertices = corners;
            // Allocate memory for face vertices
            p->vertexlist = new int[p->numberofvertices];
            // Read the vertices of the face.
            for (j = 0; j < corners; j++) {
              if (*bufferp == '\0') {
                printf("Syntax error reading face on line %d in file %s\n",
                       line_count, infilename);
                fclose(fp);
                return false;
              }
              p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0);
              // Remember the smallest index.
              if (p->vertexlist[j] < smallestidx) {
                smallestidx = p->vertexlist[j];
              }
              bufferp = findnextnumber(bufferp);
            }
            // Read the marker of the face if it exists.
            facetmarkerlist[i] = 0;
            if (*bufferp != '\0') {
              facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
            }
          }
          // Have read in a list of triangles/quads.
          numberoffacets += nfaces;
          nfaces = 0;
        } else {
          // It is a surface mesh of a tetrahedral mesh.
          if (corners == 3) {
            for (i = 0; i < numberoftrifaces; i++) {
              plist = &(trifacelist[i * 3]);
              bufferp = readline(buffer, fp, &line_count);
              if (bufferp == nullptr) {
                printf("Unexpected end of file on line %d in file %s\n",
                       line_count, infilename);
                fclose(fp);
                return false;
              }
              // Read the vertices of the face.
              for (j = 0; j < corners; j++) {
                if (*bufferp == '\0') {
                  printf("Syntax error reading face on line %d in file %s\n",
                         line_count, infilename);
                  fclose(fp);
                  return false;
                }
                plist[j] = (int) strtol(bufferp, &bufferp, 0);
                // Remember the smallest index.
                if (plist[j] < smallestidx) {
                  smallestidx = plist[j];
                }
                bufferp = findnextnumber(bufferp);
              }
              // Read the marker of the face if it exists.
              trifacemarkerlist[i] = 0;
              if (*bufferp != '\0') {
                trifacemarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
              }
            } // i
          } // if (corners == 3)
        } // if (b->refine)
      } // if (corners == 3 || corners == 4)
    }
  }

  // Close file
  fclose(fp);

  // Decide the firstnumber of the index.
  if (smallestidx == 0) {
    firstnumber = 0;  
  } else if (smallestidx == 1) {
    firstnumber = 1;
  } else {
    printf("A wrong smallest index (%d) was detected in file %s\n",
           smallestidx, infilename);
    return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_vtk()    Load VTK surface mesh from file (.vtk ascii or binary).     //
//                                                                           //
// This function is contributed by: Bryn Lloyd, Computer Vision Laborator,   //
// ETH, Zuerich. May 7, 2007.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

// Two inline functions used in read/write VTK files.

void swapBytes(unsigned char* var, int size)
{
  int i = 0;
  int j = size - 1;
  char c;

  while (i < j) {
    c = var[i]; var[i] = var[j]; var[j] = c;
    i++, j--;
  }
}

bool testIsBigEndian()
{
  short word = 0x4321;
  if((*(char *)& word) != 0x21)
    return true;
  else 
    return false;
}


bool tetgenio::load_vtk(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char line[INPUTLINESIZE];
  char mode[128], id[256], fmt[64];
  char *bufferp;
  double *coord;
  float _x, _y, _z;
  int nverts = 0;
  int nfaces = 0;
  int line_count = 0;
  int dummy;
  int id1, id2, id3;
  int nn = -1;
  int nn_old = -1;
  int i, j;
  bool ImALittleEndian = !testIsBigEndian();

  int smallestidx = 0;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) {
    strcat(infilename, ".vtk");
  }
  if (!(fp = fopen(infilename, "r"))) {
    printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  // Default uses the index starts from '0'.
  firstnumber = 0;
  strcpy(mode, "BINARY");

  while((bufferp = readline(line, fp, &line_count)) != nullptr) {
    if(strlen(line) == 0) continue;
    //swallow lines beginning with a comment sign or white space
    if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || 
       line[0] == 32) continue;

    sscanf(line, "%s", id);
    if(!strcmp(id, "ASCII")) {
      strcpy(mode, "ASCII");
    }

    if(!strcmp(id, "POINTS")) {
      sscanf(line, "%s %d %s", id, &nverts, fmt);
      if (nverts > 0) {
        numberofpoints = nverts;
        pointlist = new REAL[nverts * 3];
        smallestidx = nverts + 1;
      }

      if(!strcmp(mode, "BINARY")) {
        for(i = 0; i < nverts; i++) {
          coord = &pointlist[i * 3];
          if(!strcmp(fmt, "double")) {
            fread((char*)(&(coord[0])), sizeof(double), 1, fp);
            fread((char*)(&(coord[1])), sizeof(double), 1, fp);
            fread((char*)(&(coord[2])), sizeof(double), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0]));
              swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1]));
              swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2]));
            }
          } else if(!strcmp(fmt, "float")) {
            fread((char*)(&_x), sizeof(float), 1, fp);
            fread((char*)(&_y), sizeof(float), 1, fp);
            fread((char*)(&_z), sizeof(float), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &_x, sizeof(_x));
              swapBytes((unsigned char *) &_y, sizeof(_y));
              swapBytes((unsigned char *) &_z, sizeof(_z));
            }
            coord[0] = double(_x);
            coord[1] = double(_y);
            coord[2] = double(_z);
          } else {
            printf("Error: Only float or double formats are supported!\n");
            return false;
          }
        }
      } else if(!strcmp(mode, "ASCII")) {
        for(i = 0; i < nverts; i++){
          bufferp = readline(line, fp, &line_count);
          if (bufferp == nullptr) {
            printf("Unexpected end of file on line %d in file %s\n",
                   line_count, infilename);
            fclose(fp);
            return false;
          }
          // Read vertex coordinates
          coord = &pointlist[i * 3];
          for (j = 0; j < 3; j++) {
            if (*bufferp == '\0') {
              printf("Syntax error reading vertex coords on line");
              printf(" %d in file %s\n", line_count, infilename);
              fclose(fp);
              return false;
            }
            coord[j] = (REAL) strtod(bufferp, &bufferp);
            bufferp = findnextnumber(bufferp);
          }
        }
      }
      continue;
    }

    if(!strcmp(id, "POLYGONS")) {
      sscanf(line, "%s %d  %d", id, &nfaces, &dummy);
      if (nfaces > 0) {
        numberoffacets = nfaces;
        facetlist = new tetgenio::facet[nfaces];
      }

      if(!strcmp(mode, "BINARY")) {
        for(i = 0; i < nfaces; i++){
          fread((char*)(&nn), sizeof(int), 1, fp);
          if(ImALittleEndian){
            swapBytes((unsigned char *) &nn, sizeof(nn));
          }
          if (i == 0)
            nn_old = nn;
          if (nn != nn_old) {
            printf("Error:  No mixed cells are allowed.\n");
            return false;
          }

          if(nn == 3){
            fread((char*)(&id1), sizeof(int), 1, fp);
            fread((char*)(&id2), sizeof(int), 1, fp);
            fread((char*)(&id3), sizeof(int), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &id1, sizeof(id1));
              swapBytes((unsigned char *) &id2, sizeof(id2));
              swapBytes((unsigned char *) &id3, sizeof(id3));
            }
            f = &facetlist[i];
            init(f);
            // In .off format, each facet has one polygon, no hole.
            f->numberofpolygons = 1;
            f->polygonlist = new tetgenio::polygon[1];
            p = &f->polygonlist[0];
            init(p);
            // Set number of vertices
            p->numberofvertices = 3;
            // Allocate memory for face vertices
            p->vertexlist = new int[p->numberofvertices];
            p->vertexlist[0] = id1;
            p->vertexlist[1] = id2;
            p->vertexlist[2] = id3;
            // Detect the smallest index.
            for (j = 0; j < 3; j++) {
              if (p->vertexlist[j] < smallestidx) {
                smallestidx = p->vertexlist[j];
              }
            }
          } else {
            printf("Error: Only triangles are supported\n");
            return false;
          }
        }
      } else if(!strcmp(mode, "ASCII")) {
        for(i = 0; i < nfaces; i++) {
          bufferp = readline(line, fp, &line_count);
          nn = (int) strtol(bufferp, &bufferp, 0);
          if (i == 0)
            nn_old = nn;
          if (nn != nn_old) {
            printf("Error:  No mixed cells are allowed.\n");
            return false;
          }

          if (nn == 3) {
            bufferp = findnextnumber(bufferp); // Skip the first field.
            id1 = (int) strtol(bufferp, &bufferp, 0);
            bufferp = findnextnumber(bufferp);
            id2 = (int) strtol(bufferp, &bufferp, 0);
            bufferp = findnextnumber(bufferp);
            id3 = (int) strtol(bufferp, &bufferp, 0);
            f = &facetlist[i];
            init(f);
            // In .off format, each facet has one polygon, no hole.
            f->numberofpolygons = 1;
            f->polygonlist = new tetgenio::polygon[1];
            p = &f->polygonlist[0];
            init(p);
            // Set number of vertices
            p->numberofvertices = 3;
            // Allocate memory for face vertices
            p->vertexlist = new int[p->numberofvertices];
            p->vertexlist[0] = id1;
            p->vertexlist[1] = id2;
            p->vertexlist[2] = id3;
            // Detect the smallest index.
            for (j = 0; j < 3; j++) {
              if (p->vertexlist[j] < smallestidx) {
                smallestidx = p->vertexlist[j];
              }
            }
          } else {
            printf("Error:  Only triangles are supported.\n");
            return false;
          }
        }
      }

      fclose(fp);

      // Decide the firstnumber of the index.
      if (smallestidx == 0) {
        firstnumber = 0;  
      } else if (smallestidx == 1) {
        firstnumber = 1;
      } else {
        printf("A wrong smallest index (%d) was detected in file %s\n",
               smallestidx, infilename);
        return false;
      }

      return true;
    }

    if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){
      printf("Warning:  load_vtk(): cannot read formats LINES, CELLS.\n");
    }
  } // while ()

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_plc()    Load a piecewise linear complex from file(s).               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_plc(char* filebasename, int object)
{
  bool success;

  if (object == (int) tetgenbehavior::NODES) {
    success = load_node(filebasename);
  } else if (object == (int) tetgenbehavior::POLY) {
    success = load_poly(filebasename);
  } else if (object == (int) tetgenbehavior::OFF) {
    success = load_off(filebasename);
  } else if (object == (int) tetgenbehavior::PLY) {
    success = load_ply(filebasename);
  } else if (object == (int) tetgenbehavior::STL) {
    success = load_stl(filebasename);
  } else if (object == (int) tetgenbehavior::MEDIT) {
    success = load_medit(filebasename, 0);
  } else if (object == (int) tetgenbehavior::VTK) {
    success = load_vtk(filebasename);
  } else {
    success = load_poly(filebasename);
  }

  if (success) {
    // Try to load the following files (.edge, .var, .mtr).
    load_edge(filebasename);
    load_var(filebasename);
    load_mtr(filebasename);
  }

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_mesh()    Load a tetrahedral mesh from file(s).                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_tetmesh(char* filebasename, int object)
{
  bool success;

  if (object == (int) tetgenbehavior::MEDIT) {
    success = load_medit(filebasename, 1);
  } else {
    success = load_node(filebasename);
    if (success) {
      success = load_tet(filebasename);
    }
    if (success) {
      // Try to load the following files (.face, .edge, .vol).
      load_face(filebasename);
      load_edge(filebasename);
      load_vol(filebasename);
    }
  }

  if (success) {
    // Try to load the following files (.var, .mtr).
    load_var(filebasename);
    load_mtr(filebasename);
  }

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_nodes()    Save points to a .node file.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_nodes(char* filebasename)
{
  FILE *fout;
  char outnodefilename[FILENAMESIZE];
  char outmtrfilename[FILENAMESIZE];
  int i, j;

  sprintf(outnodefilename, "%s.node", filebasename);
  printf("Saving nodes to %s\n", outnodefilename);
  fout = fopen(outnodefilename, "w");
  fprintf(fout, "%d  %d  %d  %d\n", numberofpoints, mesh_dim,
          numberofpointattributes, pointmarkerlist != nullptr ? 1 : 0);
  for (i = 0; i < numberofpoints; i++) {
    if (mesh_dim == 2) {
      fprintf(fout, "%d  %.16g  %.16g", i + firstnumber, pointlist[i * 3],
              pointlist[i * 3 + 1]);
    } else {
      fprintf(fout, "%d  %.16g  %.16g  %.16g", i + firstnumber,
              pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]);
    }
    for (j = 0; j < numberofpointattributes; j++) {
      fprintf(fout, "  %.16g", 
              pointattributelist[i * numberofpointattributes + j]);
    }
    if (pointmarkerlist != nullptr) {
      fprintf(fout, "  %d", pointmarkerlist[i]);
    }
    fprintf(fout, "\n");
  }
  fclose(fout);

  // If the point metrics exist, output them to a .mtr file.
  if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) nullptr)) {
    sprintf(outmtrfilename, "%s.mtr", filebasename);
    printf("Saving metrics to %s\n", outmtrfilename);
    fout = fopen(outmtrfilename, "w");
    fprintf(fout, "%d  %d\n", numberofpoints, numberofpointmtrs);
    for (i = 0; i < numberofpoints; i++) {
      for (j = 0; j < numberofpointmtrs; j++) {
        fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]);
      }
      fprintf(fout, "\n");
    }
    fclose(fout);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_elements()    Save elements to a .ele file.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_elements(char* filebasename)
{
  FILE *fout;
  char outelefilename[FILENAMESIZE];
  int i, j;

  sprintf(outelefilename, "%s.ele", filebasename);
  printf("Saving elements to %s\n", outelefilename);
  fout = fopen(outelefilename, "w");
  if (mesh_dim == 3) {
    fprintf(fout, "%d  %d  %d\n", numberoftetrahedra, numberofcorners,
            numberoftetrahedronattributes);
    for (i = 0; i < numberoftetrahedra; i++) {
      fprintf(fout, "%d", i + firstnumber);
      for (j = 0; j < numberofcorners; j++) {
        fprintf(fout, "  %5d", tetrahedronlist[i * numberofcorners + j]);
      }
      for (j = 0; j < numberoftetrahedronattributes; j++) {
        fprintf(fout, "  %g",
          tetrahedronattributelist[i * numberoftetrahedronattributes + j]);
      }
      fprintf(fout, "\n");
    }
  } else {
    // Save a two-dimensional mesh.
    fprintf(fout, "%d  %d  %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0);
    for (i = 0; i < numberoftrifaces; i++) {
      fprintf(fout, "%d", i + firstnumber);
      for (j = 0; j < 3; j++) {
        fprintf(fout, "  %5d", trifacelist[i * 3 + j]);
      }
      if (trifacemarkerlist != nullptr) {
        fprintf(fout, "  %d", trifacemarkerlist[i]);
      }
      fprintf(fout, "\n");
    }
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_faces()    Save faces to a .face file.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_faces(char* filebasename)
{
  FILE *fout;
  char outfacefilename[FILENAMESIZE];
  int i;

  sprintf(outfacefilename, "%s.face", filebasename);
  printf("Saving faces to %s\n", outfacefilename);
  fout = fopen(outfacefilename, "w");
  fprintf(fout, "%d  %d\n", numberoftrifaces, 
          trifacemarkerlist != nullptr ? 1 : 0);
  for (i = 0; i < numberoftrifaces; i++) {
    fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber, trifacelist[i * 3],
            trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]);
    if (trifacemarkerlist != nullptr) {
      fprintf(fout, "  %d", trifacemarkerlist[i]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_edges()    Save egdes to a .edge file.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_edges(char* filebasename)
{
  FILE *fout;
  char outedgefilename[FILENAMESIZE];
  int i;

  sprintf(outedgefilename, "%s.edge", filebasename);
  printf("Saving edges to %s\n", outedgefilename);
  fout = fopen(outedgefilename, "w");
  fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != nullptr ? 1 : 0);
  for (i = 0; i < numberofedges; i++) {
    fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
            edgelist[i * 2 + 1]);
    if (edgemarkerlist != nullptr) {
      fprintf(fout, "  %d", edgemarkerlist[i]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_neighbors()    Save egdes to a .neigh file.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_neighbors(char* filebasename)
{
  FILE *fout;
  char outneighborfilename[FILENAMESIZE];
  int i;

  sprintf(outneighborfilename, "%s.neigh", filebasename);
  printf("Saving neighbors to %s\n", outneighborfilename);
  fout = fopen(outneighborfilename, "w");
  fprintf(fout, "%d  %d\n", numberoftetrahedra, mesh_dim + 1);
  for (i = 0; i < numberoftetrahedra; i++) {
    if (mesh_dim == 2) {
      fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber,  neighborlist[i * 3],
              neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]);
    } else {
      fprintf(fout, "%d  %5d  %5d  %5d  %5d", i + firstnumber,
              neighborlist[i * 4], neighborlist[i * 4 + 1],
              neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_poly()    Save segments or facets to a .poly file.                   //
//                                                                           //
// It only save the facets, holes and regions. No .node file is saved.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_poly(char* filebasename)
{
  FILE *fout;
  facet *f;
  polygon *p;
  char outpolyfilename[FILENAMESIZE];
  int i, j, k;

  sprintf(outpolyfilename, "%s.poly", filebasename);
  printf("Saving poly to %s\n", outpolyfilename);
  fout = fopen(outpolyfilename, "w");

  // The zero indicates that the vertices are in a separate .node file.
  //   Followed by number of dimensions, number of vertex attributes,
  //   and number of boundary markers (zero or one).
  fprintf(fout, "%d  %d  %d  %d\n", 0, mesh_dim, numberofpointattributes,
          pointmarkerlist != nullptr ? 1 : 0);

  // Save segments or facets.
  if (mesh_dim == 2) {
    // Number of segments, number of boundary markers (zero or one).
    fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != nullptr ? 1 : 0);
    for (i = 0; i < numberofedges; i++) {
      fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
              edgelist[i * 2 + 1]);
      if (edgemarkerlist != nullptr) {
        fprintf(fout, "  %d", edgemarkerlist[i]);
      }
      fprintf(fout, "\n");
    }
  } else {
    // Number of facets, number of boundary markers (zero or one).
    fprintf(fout, "%d  %d\n", numberoffacets, facetmarkerlist != nullptr ? 1 : 0);
    for (i = 0; i < numberoffacets; i++) {
      f = &(facetlist[i]);
      fprintf(fout, "%d  %d  %d  # %d\n", f->numberofpolygons,f->numberofholes,
            facetmarkerlist != nullptr ? facetmarkerlist[i] : 0, i + firstnumber);
      // Output polygons of this facet.
      for (j = 0; j < f->numberofpolygons; j++) {
        p = &(f->polygonlist[j]);
        fprintf(fout, "%d  ", p->numberofvertices);
        for (k = 0; k < p->numberofvertices; k++) {
          if (((k + 1) % 10) == 0) {
            fprintf(fout, "\n  ");
          }
          fprintf(fout, "  %d", p->vertexlist[k]);
        }
        fprintf(fout, "\n");
      }
      // Output holes of this facet.
      for (j = 0; j < f->numberofholes; j++) {
        fprintf(fout, "%d  %.12g  %.12g  %.12g\n", j + firstnumber,
           f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]);
      }
    }
  }

  // Save holes.
  fprintf(fout, "%d\n", numberofholes);
  for (i = 0; i < numberofholes; i++) {
    // Output x, y coordinates.
    fprintf(fout, "%d  %.12g  %.12g", i + firstnumber, holelist[i * mesh_dim],
            holelist[i * mesh_dim + 1]);
    if (mesh_dim == 3) {
      // Output z coordinate.
      fprintf(fout, "  %.12g", holelist[i * mesh_dim + 2]);
    }
    fprintf(fout, "\n");
  }

  // Save regions.
  fprintf(fout, "%d\n", numberofregions);
  for (i = 0; i < numberofregions; i++) {
    if (mesh_dim == 2) {
      // Output the index, x, y coordinates, attribute (region number)
      //   and maximum area constraint (maybe -1).
      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
              regionlist[i * 4], regionlist[i * 4 + 1],
              regionlist[i * 4 + 2], regionlist[i * 4 + 3]);
    } else {
      // Output the index, x, y, z coordinates, attribute (region number)
      //   and maximum volume constraint (maybe -1).
      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
              regionlist[i * 5], regionlist[i * 5 + 1],
              regionlist[i * 5 + 2], regionlist[i * 5 + 3],
              regionlist[i * 5 + 4]);
    }
  }

  fclose(fout);  
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_faces2smesh()    Save triangular faces to a .smesh file.             //
//                                                                           //
// It only save the facets. No holes and regions. No .node file.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_faces2smesh(char* filebasename)
{
  FILE *fout;
  char outsmeshfilename[FILENAMESIZE];
  int i, j;

  sprintf(outsmeshfilename, "%s.smesh", filebasename);
  printf("Saving faces to %s\n", outsmeshfilename);
  fout = fopen(outsmeshfilename, "w");

  // The zero indicates that the vertices are in a separate .node file.
  //   Followed by number of dimensions, number of vertex attributes,
  //   and number of boundary markers (zero or one).
  fprintf(fout, "%d  %d  %d  %d\n", 0, mesh_dim, numberofpointattributes,
          pointmarkerlist != nullptr ? 1 : 0);

  // Number of facets, number of boundary markers (zero or one).
  fprintf(fout, "%d  %d\n", numberoftrifaces, 
          trifacemarkerlist != nullptr ? 1 : 0);

  // Output triangular facets.
  for (i = 0; i < numberoftrifaces; i++) {
    j = i * 3;
    fprintf(fout, "3  %d %d %d", trifacelist[j], trifacelist[j + 1], 
            trifacelist[j + 2]);
    if (trifacemarkerlist != nullptr) {
      fprintf(fout, "  %d", trifacemarkerlist[i]);
    }
    fprintf(fout, "\n");
  }

  // No holes and regions.
  fprintf(fout, "0\n");
  fprintf(fout, "0\n");

  fclose(fout);  
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// readline()   Read a nonempty line from a file.                            //
//                                                                           //
// A line is considered "nonempty" if it contains something more than white  //
// spaces.  If a line is considered empty, it will be dropped and the next   //
// line will be read, this process ends until reaching the end-of-file or a  //
// non-empty line.  Return nullptr if it is the end-of-file, otherwise, return  //
// a pointer to the first non-whitespace character of the line.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::readline(char *string, FILE *infile, int *linenumber)
{
  char *result;

  // Search for a non-empty line.
  do {
    result = fgets(string, INPUTLINESIZE - 1, infile);
    if (linenumber) (*linenumber)++;
    if (result == (char *) nullptr) {
      return (char *) nullptr;
    }
    // Skip white spaces.
    while ((*result == ' ') || (*result == '\t')) result++;
    // If it's end of line, read another line and try again.
  } while ((*result == '\0') || (*result == '\r') || (*result == '\n'));
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findnextfield()   Find the next field of a string.                        //
//                                                                           //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::findnextfield(char *string)
{
  char *result;

  result = string;
  // Skip the current field.  Stop upon reaching whitespace or a comma.
  while ((*result != '\0') && (*result != ' ') &&  (*result != '\t') && 
         (*result != ',') && (*result != ';')) {
    result++;
  }
  // Now skip the whitespace or the comma, stop at anything else that looks
  //   like a character, or the end of a line. 
  while ((*result == ' ') || (*result == '\t') || (*result == ',') ||
         (*result == ';')) {
    result++;
  }
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// readnumberline()   Read a nonempty number line from a file.               //
//                                                                           //
// A line is considered "nonempty" if it contains something that looks like  //
// a number.  Comments (prefaced by `#') are ignored.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename)
{
  char *result;

  // Search for something that looks like a number.
  do {
    result = fgets(string, INPUTLINESIZE, infile);
    if (result == (char *) nullptr) {
      return result;
    }
    // Skip anything that doesn't look like a number, a comment, 
    //   or the end of a line. 
    while ((*result != '\0') && (*result != '#')
           && (*result != '.') && (*result != '+') && (*result != '-')
           && ((*result < '0') || (*result > '9'))) {
      result++;
    }
    // If it's a comment or end of line, read another line and try again.
  } while ((*result == '#') || (*result == '\0'));
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findnextnumber()   Find the next field of a number string.                //
//                                                                           //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field that looks  //
// like a number.                                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::findnextnumber(char *string)
{
  char *result;

  result = string;
  // Skip the current field.  Stop upon reaching whitespace or a comma.
  while ((*result != '\0') && (*result != '#') && (*result != ' ') && 
         (*result != '\t') && (*result != ',')) {
    result++;
  }
  // Now skip the whitespace and anything else that doesn't look like a
  //   number, a comment, or the end of a line. 
  while ((*result != '\0') && (*result != '#')
         && (*result != '.') && (*result != '+') && (*result != '-')
         && ((*result < '0') || (*result > '9'))) {
    result++;
  }
  // Check for a comment (prefixed with `#').
  if (*result == '#') {
    *result = '\0';
  }
  return result;
}

////                                                                       ////
////                                                                       ////
//// io_cxx ///////////////////////////////////////////////////////////////////

//// behavior_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// syntax()    Print list of command line switches.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::syntax()
{
  printf("  tetgen [-pYq_Aa_mriO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n");
  printf("    -p  Tetrahedralizes a piecewise linear complex (PLC).\n");
  printf("    -Y  Preserves the input surface mesh (does not modify it).\n");
  printf("    -q  Refines mesh (to improve mesh quality).\n");
  printf("    -A  Assigns attributes to tetrahedra in different regions.\n");
  printf("    -a  Applies a maximum tetrahedron volume constraint.\n");
  printf("    -m  Applies a mesh sizing function.\n");
  printf("    -r  Reconstructs a previously generated mesh.\n");
  printf("    -i  Inserts a list of additional points.\n");
  printf("    -O  Specifies the level of mesh optimization.\n");
  printf("    -S  Specifies maximum number of added points.\n");
  printf("    -T  Sets a tolerance for coplanar test (default 1e-8).\n");
  printf("    -X  Suppresses use of exact arithmetic.\n");
  printf("    -M  No merge of coplanar facets or very close vertices.\n");
  printf("    -w  Generates weighted Delaunay (regular) triangulation.\n");
  printf("    -c  Retains the convex hull of the PLC.\n");
  printf("    -d  Detects self-intersections of facets of the PLC.\n");
  printf("    -z  Numbers all output items starting from zero.\n");
  printf("    -f  Outputs all faces to .face file.\n");
  printf("    -e  Outputs all edges to .edge file.\n");
  printf("    -n  Outputs tetrahedra neighbors to .neigh file.\n");
  printf("    -v  Outputs Voronoi diagram to files.\n");
  printf("    -g  Outputs mesh to .mesh file for viewing by Medit.\n");
  printf("    -k  Outputs mesh to .vtk file for viewing by Paraview.\n");
  printf("    -J  No jettison of unused vertices from output .node file.\n");
  printf("    -B  Suppresses output of boundary information.\n");
  printf("    -N  Suppresses output of .node file.\n");
  printf("    -E  Suppresses output of .ele file.\n");
  printf("    -F  Suppresses output of .face and .edge file.\n");
  printf("    -I  Suppresses mesh iteration numbers.\n");
  printf("    -C  Checks the consistency of the final mesh.\n");
  printf("    -Q  Quiet:  No terminal output except errors.\n");
  printf("    -V  Verbose:  Detailed information, more terminal output.\n");
  printf("    -h  Help:  A brief instruction for using TetGen.\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// usage()    Print a brief instruction for using TetGen.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::usage()
{
  printf("TetGen\n");
  printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay ");
  printf("Triangulator\n");
  printf("Version 1.5\n");
  printf("(June 21, 2013)\n");
  printf("\n");
  printf("Copyright (C) 2002 - 2013\n");
  printf("Hang Si\n");
  printf("Mohrenstr. 39, 10117 Berlin, Germany\n");
  printf("si@wias-berlin.de\n");
  printf("\n");
  printf("What Can TetGen Do?\n");
  printf("\n");
  printf("  TetGen generates Delaunay tetrahedralizations, constrained\n");
  printf("  Delaunay tetrahedralizations, and quality tetrahedral meshes.\n");
  printf("\n");
  printf("Command Line Syntax:\n");
  printf("\n");
  printf("  Below is the basic command line syntax of TetGen with a list of ");
  printf("short\n");
  printf("  descriptions. Underscores indicate that numbers may optionally\n");
  printf("  follow certain switches.  Do not leave any space between a ");
  printf("switch\n");
  printf("  and its numeric parameter.  \'input_file\' contains input data\n");
  printf("  depending on the switches you supplied which may be a ");
  printf("  piecewise\n");
  printf("  linear complex or a list of nodes.  File formats and detailed\n");
  printf("  description of command line switches are found in user's ");
  printf("manual.\n");
  printf("\n");
  syntax();
  printf("\n");
  printf("Examples of How to Use TetGen:\n");
  printf("\n");
  printf("  \'tetgen object\' reads vertices from object.node, and writes ");
  printf("their\n  Delaunay tetrahedralization to object.1.node, ");
  printf("object.1.ele\n  (tetrahedra), and object.1.face");
  printf(" (convex hull faces).\n");
  printf("\n");
  printf("  \'tetgen -p object\' reads a PLC from object.poly or object.");
  printf("smesh (and\n  possibly object.node) and writes its constrained ");
  printf("Delaunay\n  tetrahedralization to object.1.node, object.1.ele, ");
  printf("object.1.face,\n");
  printf("  (boundary faces) and object.1.edge (boundary edges).\n");
  printf("\n");
  printf("  \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n");
  printf("  object.smesh (and possibly object.node), generates a mesh ");
  printf("whose\n  tetrahedra have radius-edge ratio smaller than 1.414 and ");
  printf("have volume\n  of 0.1 or less, and writes the mesh to ");
  printf("object.1.node, object.1.ele,\n  object.1.face, and object.1.edge\n");
  printf("\n");
  printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n");
  terminatetetgen(0);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// parse_commandline()    Read the command line, identify switches, and set  //
//                        up options and file names.                         //
//                                                                           //
// 'argc' and 'argv' are the same parameters passed to the function main()   //
// of a C/C++ program. They together represent the command line user invoked //
// from an environment in which TetGen is running.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenbehavior::parse_commandline(int argc, char **argv)
{
  int startindex;
  int increment;
  int meshnumber;
  int i, j, k;
  char workstring[1024];

  // First determine the input style of the switches.
  if (argc == 0) {
    startindex = 0;                    // Switches are given without a dash.
    argc = 1;                    // For running the following for-loop once.
    commandline[0] = '\0';
  } else {
    startindex = 1;
    strcpy(commandline, argv[0]);
    strcat(commandline, " ");
  }

  for (i = startindex; i < argc; i++) {
    // Remember the command line for output.
    strcat(commandline, argv[i]);
    strcat(commandline, " ");
    if (startindex == 1) {
      // Is this string a filename?
      if (argv[i][0] != '-') {
        strncpy(infilename, argv[i], 1024 - 1);
        infilename[1024 - 1] = '\0';
        continue;                     
      }
    }
    // Parse the individual switch from the string.
    for (j = startindex; argv[i][j] != '\0'; j++) {
      if (argv[i][j] == 'p') {
        plc = 1;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          facet_ang_tol = (REAL) strtod(workstring, (char **) nullptr);
        }
      } else if (argv[i][j] == 's') {
        psc = 1;
      } else if (argv[i][j] == 'Y') {
        nobisect = 1;
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
          nobisect_param = (argv[i][j + 1] - '0');
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
            addsteiner_algo = (argv[i][j + 1] - '0');
            j++;
          }
        }
      } else if (argv[i][j] == 'r') {
        refine = 1;
      } else if (argv[i][j] == 'q') {
        quality = 1;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          minratio = (REAL) strtod(workstring, (char **) nullptr);
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            mindihedral = (REAL) strtod(workstring, (char **) nullptr);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            optmaxdihedral = (REAL) strtod(workstring, (char **) nullptr);
          }
        }
      } else if (argv[i][j] == 'w') {
        weighted = 1;
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
          weighted_param = (argv[i][j + 1] - '0');
          j++;
        }
      } else if (argv[i][j] == 'b') {
        // -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order)
        brio_hilbert = 1;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          brio_threshold = (int) strtol(workstring, (char **) &workstring, 0);
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            brio_ratio = (REAL) strtod(workstring, (char **) nullptr);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            hilbert_order = (int)strtod(workstring, (char **) nullptr);
          }
        }
        if (brio_threshold == 0) { // -b0
          brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. 
        }
        if (brio_ratio >= 1.0) { // -b/1
          no_sort = 1;
          brio_hilbert = 0; // Turn off BRIO-Hilbert sorting.
        }
      } else if (argv[i][j] == 'l') {
        incrflip = 1;
      } else if (argv[i][j] == 'L') {
        flipinsert = 1;
      } else if (argv[i][j] == 'm') {
        metric = 1;
      } else if (argv[i][j] == 'a') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          fixedvolume = 1;
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          maxvolume = (REAL) strtod(workstring, (char **) nullptr);
        } else {
          varvolume = 1;
        }
      } else if (argv[i][j] == 'A') {
        regionattrib = 1;
      } else if (argv[i][j] == 'D') {
        conforming = 1;
        if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) {
          reflevel = (argv[i][j + 1] - '1') + 1; 
          j++;
        }
      } else if (argv[i][j] == 'i') {
        insertaddpoints = 1;
      } else if (argv[i][j] == 'd') {
        diagnose = 1;
      } else if (argv[i][j] == 'c') {
        convex = 1;
      } else if (argv[i][j] == 'M') {
        nomergefacet = 1;
        nomergevertex = 1;
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) {
          nomergefacet = (argv[i][j + 1] - '0');
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) {
            nomergevertex = (argv[i][j + 1] - '0');
            j++;
          }
        }
      } else if (argv[i][j] == 'X') {
        if (argv[i][j + 1] == '1') {
          nostaticfilter = 1;
          j++;
        } else {
          noexact = 1;
        }
      } else if (argv[i][j] == 'z') {
        zeroindex = 1;
      } else if (argv[i][j] == 'f') {
        facesout++;
      } else if (argv[i][j] == 'e') {
        edgesout++;
      } else if (argv[i][j] == 'n') {
        neighout++;
      } else if (argv[i][j] == 'v') {
        voroout = 1;
      } else if (argv[i][j] == 'g') {
        meditview = 1;
      } else if (argv[i][j] == 'k') {
        vtkview = 1;  
      } else if (argv[i][j] == 'J') {
        nojettison = 1;
      } else if (argv[i][j] == 'B') {
        nobound = 1;
      } else if (argv[i][j] == 'N') {
        nonodewritten = 1;
      } else if (argv[i][j] == 'E') {
        noelewritten = 1;
      } else if (argv[i][j] == 'F') {
        nofacewritten = 1;
      } else if (argv[i][j] == 'I') {
        noiterationnum = 1;
      } else if (argv[i][j] == 'S') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          steinerleft = (int) strtol(workstring, (char **) nullptr, 0);
        }
      } else if (argv[i][j] == 'o') {
        if (argv[i][j + 1] == '2') {
          order = 2;
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
              (argv[i][j + 1] == '.')) {
            k = 0;
            while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                   (argv[i][j + 1] == '.')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            optmaxdihedral = (REAL) strtod(workstring, (char **) nullptr);
          }
        }
      } else if (argv[i][j] == 'O') {
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
          optlevel = (argv[i][j + 1] - '0');
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) {
            optscheme = (argv[i][j + 1] - '0');
            j++;
          }
        }
      } else if (argv[i][j] == 'T') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          epsilon = (REAL) strtod(workstring, (char **) nullptr);
        }
      } else if (argv[i][j] == 'R') {
        reversetetori = 1;
      } else if (argv[i][j] == 'C') {
        docheck++;
      } else if (argv[i][j] == 'Q') {
        quiet = 1;
      } else if (argv[i][j] == 'V') {
        verbose++;
      } else if (argv[i][j] == 'x') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          tetrahedraperblock = (int) strtol(workstring, (char **) nullptr, 0);
          if (tetrahedraperblock > 8188) {
            vertexperblock = tetrahedraperblock / 2;
            shellfaceperblock = vertexperblock / 2;
          } else {
            tetrahedraperblock = 8188;
          }
        }
      } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
                 (argv[i][j] == '?')) {
        usage();
      } else {
        printf("Warning:  Unknown switch -%c.\n", argv[i][j]);
      }
    }
  }

  if (startindex == 0) {
    // Set a temporary filename for debugging output.
    strcpy(infilename, "tetgen-tmpfile");
  } else {
    if (infilename[0] == '\0') {
      // No input file name. Print the syntax and exit.
      syntax();
      terminatetetgen(0);
    }
    // Recognize the object from file extension if it is available.
    if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = NODES;
    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = POLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) {
      infilename[strlen(infilename) - 6] = '\0';
      object = POLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = OFF;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = PLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = STL;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = MEDIT;
      if (!refine) plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = VTK;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = MESH;
      refine = 1;
    }
  }

  if (nobisect && (!plc && !refine)) { // -Y
    plc = 1; // Default -p option.
  }
  if (quality && (!plc && !refine)) { // -q
    plc = 1; // Default -p option.
  }
  if (diagnose && !plc) { // -d
    plc = 1;
  }
  if (plc && !quality && !nobisect) { // -p only
    // Create a CDT, do not do mesh optimization.
    optlevel = 0;
  }
  if (refine && !quality) { // -r only
    // Reconstruct a mesh, no mesh optimization.
    optlevel = 0;
  }

  // Detect improper combinations of switches.
  if (plc && refine) {
    printf("Error:  Switch -r cannot use together with -p.\n");
    return false;
  }
  if (refine && (plc || noiterationnum)) {
    printf("Error:  Switches %s cannot use together with -r.\n",
           "-p, -d, and -I");
    return false;
  }
  if ((refine || plc) && weighted) {
    printf("Error:  Switches -w cannot use together with -p or -r.\n");
    return false;
  }

  if (convex) { // -c
    if (plc && !regionattrib) {
      // -A (region attribute) is needed for marking exterior tets (-1).
      regionattrib = 1; 
    }
  }

  // Note: -A must not used together with -r option. 
  // Be careful not to add an extra attribute to each element unless the
  //   input supports it (PLC in, but not refining a preexisting mesh).
  if (refine || !plc) {
    regionattrib = 0;
  }
  // Be careful not to allocate space for element area constraints that 
  //   will never be assigned any value (other than the default -1.0).
  if (!refine && !plc) {
    varvolume = 0;
  }
  // If '-a' or '-aa' is in use, enable '-q' option too.
  if (fixedvolume || varvolume) {
    if (quality == 0) {
      quality = 1;
      if (!plc && !refine) {
        plc = 1; // enable -p.
      }
    }
  }
  // No user-specified dihedral angle bound. Use default ones.
  if (!quality) {
    if (optmaxdihedral < 179.0) {
      optmaxdihedral = 179.0;
    }
    if (optminsmtdihed < 179.999) {
      optminsmtdihed = 179.999;
    }
    if (optminslidihed < 179.999) {
      optminslidihed = 179.999;
    }
  }

  increment = 0;
  strcpy(workstring, infilename);
  j = 1;
  while (workstring[j] != '\0') {
    if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
      increment = j + 1;
    }
    j++;
  }
  meshnumber = 0;
  if (increment > 0) {
    j = increment;
    do {
      if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
        meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
      } else {
        increment = 0;
      }
      j++;
    } while (workstring[j] != '\0');
  }
  if (noiterationnum) {
    strcpy(outfilename, infilename);
  } else if (increment == 0) {
    strcpy(outfilename, infilename);
    strcat(outfilename, ".1");
  } else {
    workstring[increment] = '%';
    workstring[increment + 1] = 'd';
    workstring[increment + 2] = '\0';
    sprintf(outfilename, workstring, meshnumber + 1);
  }
  // Additional input file name has the end ".a".
  strcpy(addinfilename, infilename);
  strcat(addinfilename, ".a");
  // Background filename has the form "*.b.ele", "*.b.node", ...
  strcpy(bgmeshfilename, infilename);
  strcat(bgmeshfilename, ".b");

  return true;
}

////                                                                       ////
////                                                                       ////
//// behavior_cxx /////////////////////////////////////////////////////////////

//// mempool_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

// Initialize fast lookup tables for mesh maniplulation primitives.

int tetgenmesh::bondtbl[12][12] = {{0,},};
int tetgenmesh::enexttbl[12] = {0,};
int tetgenmesh::eprevtbl[12] = {0,};
int tetgenmesh::enextesymtbl[12] = {0,};
int tetgenmesh::eprevesymtbl[12] = {0,};
int tetgenmesh::eorgoppotbl[12] = {0,};
int tetgenmesh::edestoppotbl[12] = {0,};
int tetgenmesh::fsymtbl[12][12] = {{0,},};
int tetgenmesh::facepivot1[12] = {0,};
int tetgenmesh::facepivot2[12][12] = {{0,},};
int tetgenmesh::tsbondtbl[12][6] = {{0,},};
int tetgenmesh::stbondtbl[12][6] = {{0,},};
int tetgenmesh::tspivottbl[12][6] = {{0,},};
int tetgenmesh::stpivottbl[12][6] = {{0,},};

// Table 'esymtbl' takes an directed edge (version) as input, returns the
//   inversed edge (version) of it.

int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2};

// The following four tables give the 12 permutations of the set {0,1,2,3}.
//   An offset 4 is added to each element for a direct access of the points
//   in the tetrahedron data structure.

int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4};
int tetgenmesh::destpivot[12] = {6, 4, 4, 6, 5, 6, 7, 4, 7, 7, 5, 5};
int tetgenmesh::apexpivot[12] = {5, 6, 7, 4, 7, 7, 5, 5, 6, 4, 4, 6};
int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7};

// The twelve versions correspond to six undirected edges. The following two
//   tables map a version to an undirected edge and vice versa.

int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2};
int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5};

// Edge versions whose apex or opposite may be dummypoint.

int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11};


// Table 'snextpivot' takes an edge version as input, returns the next edge
//   version in the same edge ring.

int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3};

// The following three tables give the 6 permutations of the set {0,1,2}.
//   An offset 3 is added to each element for a direct access of the points
//   in the triangle data structure.

int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3};
int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5};
int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4};

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// inittable()    Initialize the look-up tables.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::inittables()
{
  int i, j;


  // i = t1.ver; j = t2.ver;
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 12; j++) {
      bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12);
    }
  }


  // i = t1.ver; j = t2.ver
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 12; j++) {
      fsymtbl[i][j] = (j + 12 - (i & 12)) % 12;
    }
  }


  for (i = 0; i < 12; i++) {
    facepivot1[i] = (esymtbl[i] & 3);
  }

  for (i = 0; i < 12; i++) {
    for (j = 0; j < 12; j++) {
      facepivot2[i][j] = fsymtbl[esymtbl[i]][j];
    }
  }

  for (i = 0; i < 12; i++) {
    enexttbl[i] = (i + 4) % 12;
    eprevtbl[i] = (i + 8) % 12;
  }

  for (i = 0; i < 12; i++) {
    enextesymtbl[i] = esymtbl[enexttbl[i]];
    eprevesymtbl[i] = esymtbl[eprevtbl[i]];
  }

  for (i = 0; i < 12; i++) {
    eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]];
    edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]];
  }

  int soffset, toffset;

  // i = t.ver, j = s.shver
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 6; j++) {
      if ((j & 1) == 0) {
        soffset = (6 - ((i & 12) >> 1)) % 6;
        toffset = (12 - ((j & 6) << 1)) % 12;
      } else {
        soffset = (i & 12) >> 1;
        toffset = (j & 6) << 1;
      }
      tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
      stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
    }
  }


  // i = t.ver, j = s.shver
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 6; j++) {
      if ((j & 1) == 0) {
        soffset = (i & 12) >> 1;
        toffset = (j & 6) << 1;
      } else {
        soffset = (6 - ((i & 12) >> 1)) % 6;
        toffset = (12 - ((j & 6) << 1)) % 12;
      }
      tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
      stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restart()    Deallocate all objects in this pool.                         //
//                                                                           //
// The pool returns to a fresh state, like after it was initialized, except  //
// that no memory is freed to the operating system.  Rather, the previously  //
// allocated blocks are ready to be used.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::arraypool::restart()
{
  objects = 0l;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// poolinit()    Initialize an arraypool for allocation of objects.          //
//                                                                           //
// Before the pool may be used, it must be initialized by this procedure.    //
// After initialization, memory can be allocated and freed in this pool.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk)
{
  // Each object must be at least one byte long.
  objectbytes = sizeofobject > 1 ? sizeofobject : 1;

  log2objectsperblock = log2objperblk;
  // Compute the number of objects in each block.
  objectsperblock = ((int) 1) << log2objectsperblock;
  objectsperblockmark = objectsperblock - 1;

  // No memory has been allocated.
  totalmemory = 0l;
  // The top array has not been allocated yet.
  toparray = (char **) nullptr;
  toparraylen = 0;

  // Ready all indices to be allocated.
  restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// arraypool()    The constructor and destructor.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk)
{
  poolinit(sizeofobject, log2objperblk);
}

tetgenmesh::arraypool::~arraypool()
{
  int i;

  // Has anything been allocated at all?
  if (toparray != (char **) nullptr) {
    // Walk through the top array.
    for (i = 0; i < toparraylen; i++) {
      // Check every pointer; nullptrs may be scattered randomly.
      if (toparray[i] != (char *) nullptr) {
        // Free an allocated block.
        free((void *) toparray[i]);
      }
    }
    // Free the top array.
    free((void *) toparray);
  }

  // The top array is no longer allocated.
  toparray = (char **) nullptr;
  toparraylen = 0;
  objects = 0;
  totalmemory = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getblock()    Return (and perhaps create) the block containing the object //
//               with a given index.                                         //
//                                                                           //
// This function takes care of allocating or resizing the top array if nece- //
// ssary, and of allocating the block if it hasn't yet been allocated.       //
//                                                                           //
// Return a pointer to the beginning of the block (NOT the object).          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenmesh::arraypool::getblock(int objectindex)
{
  char **newarray;
  char *block;
  int newsize;
  int topindex;
  int i;

  // Compute the index in the top array (upper bits).
  topindex = objectindex >> log2objectsperblock;
  // Does the top array need to be allocated or resized?
  if (toparray == (char **) nullptr) {
    // Allocate the top array big enough to hold 'topindex', and nullptr out
    //   its contents.
    newsize = topindex + 128;
    toparray = (char **) malloc((size_t) (newsize * sizeof(char *)));
    toparraylen = newsize;
    for (i = 0; i < newsize; i++) {
      toparray[i] = (char *) nullptr;
    }
    // Account for the memory.
    totalmemory = newsize * (uintptr_t) sizeof(char *);
  } else if (topindex >= toparraylen) {
    // Resize the top array, making sure it holds 'topindex'.
    newsize = 3 * toparraylen;
    if (topindex >= newsize) {
      newsize = topindex + 128;
    }
    // Allocate the new array, copy the contents, nullptr out the rest, and
    //   free the old array.
    newarray = (char **) malloc((size_t) (newsize * sizeof(char *)));
    for (i = 0; i < toparraylen; i++) {
      newarray[i] = toparray[i];
    }
    for (i = toparraylen; i < newsize; i++) {
      newarray[i] = (char *) nullptr;
    }
    free(toparray);
    // Account for the memory.
    totalmemory += (newsize - toparraylen) * sizeof(char *);
    toparray = newarray;
    toparraylen = newsize;
  }

  // Find the block, or learn that it hasn't been allocated yet.
  block = toparray[topindex];
  if (block == (char *) nullptr) {
    // Allocate a block at this index.
    block = (char *) malloc((size_t) (objectsperblock * objectbytes));
    toparray[topindex] = block;
    // Account for the memory.
    totalmemory += objectsperblock * objectbytes;
  }

  // Return a pointer to the block.
  return block;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lookup()    Return the pointer to the object with a given index, or nullptr  //
//             if the object's block doesn't exist yet.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::arraypool::lookup(int objectindex)
{
  char *block;
  int topindex;

  // Has the top array been allocated yet?
  if (toparray == (char **) nullptr) {
    return (void *) nullptr;
  }

  // Compute the index in the top array (upper bits).
  topindex = objectindex >> log2objectsperblock;
  // Does the top index fit in the top array?
  if (topindex >= toparraylen) {
    return (void *) nullptr;
  }

  // Find the block, or learn that it hasn't been allocated yet.
  block = toparray[topindex];
  if (block == (char *) nullptr) {
    return (void *) nullptr;
  }

  // Compute a pointer to the object with the given index.  Note that
  //   'objectsperblock' is a power of two, so the & operation is a bit mask
  //   that preserves the lower bits.
  return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// newindex()    Allocate space for a fresh object from the pool.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::arraypool::newindex(void **newptr)
{
  void *newobject;
  int newindex;

  // Allocate an object at index 'firstvirgin'.
  newindex = objects;
  newobject = (void *) (getblock(objects) +
    (objects & (objectsperblock - 1)) * objectbytes);
  objects++;

  // If 'newptr' is not nullptr, use it to return a pointer to the object.
  if (newptr != (void **) nullptr) {
    *newptr = newobject;
  }
  return newindex;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// memorypool()   The constructors of memorypool.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::memorypool::memorypool()
{
  firstblock = nowblock = (void **) nullptr;
  nextitem = (void *) nullptr;
  deaditemstack = (void *) nullptr;
  pathblock = (void **) nullptr;
  pathitem = (void *) nullptr;
  alignbytes = 0;
  itembytes = itemwords = 0;
  itemsperblock = 0;
  items = maxitems = 0l;
  unallocateditems = 0;
  pathitemsleft = 0;
}

tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, 
                                   int alignment)
{
  poolinit(bytecount, itemcount, wsize, alignment);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// ~memorypool()   Free to the operating system all memory taken by a pool.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::memorypool::~memorypool()
{
  while (firstblock != (void **) nullptr) {
    nowblock = (void **) *(firstblock);
    free(firstblock);
    firstblock = nowblock;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// poolinit()    Initialize a pool of memory for allocation of items.        //
//                                                                           //
// A `pool' is created whose records have size at least `bytecount'.  Items  //
// will be allocated in `itemcount'-item blocks.  Each item is assumed to be //
// a collection of words, and either pointers or floating-point values are   //
// assumed to be the "primary" word type.  (The "primary" word type is used  //
// to determine alignment of items.)  If `alignment' isn't zero, all items   //
// will be `alignment'-byte aligned in memory.  `alignment' must be either a //
// multiple or a factor of the primary word size;  powers of two are safe.   //
// `alignment' is normally used to create a few unused bits at the bottom of //
// each item's pointer, in which information may be stored.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize,
                                      int alignment)
{
  // Find the proper alignment, which must be at least as large as:
  //   - The parameter `alignment'.
  //   - The primary word type, to avoid unaligned accesses.
  //   - sizeof(void *), so the stack of dead items can be maintained
  //       without unaligned accesses.
  if (alignment > wordsize) {
    alignbytes = alignment;
  } else {
    alignbytes = wordsize;
  }
  if ((int) sizeof(void *) > alignbytes) {
    alignbytes = (int) sizeof(void *);
  }
  itemwords = ((bytecount + alignbytes - 1) /  alignbytes)
            * (alignbytes / wordsize);
  itembytes = itemwords * wordsize;
  itemsperblock = itemcount;

  // Allocate a block of items.  Space for `itemsperblock' items and one
  //   pointer (to point to the next block) are allocated, as well as space
  //   to ensure alignment of the items. 
  firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *)
                                + alignbytes); 
  if (firstblock == (void **) nullptr) {
    terminatetetgen(1);
  }
  // Set the next block pointer to nullptr.
  *(firstblock) = (void *) nullptr;
  restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restart()   Deallocate all items in this pool.                            //
//                                                                           //
// The pool is returned to its starting state, except that no memory is      //
// freed to the operating system.  Rather, the previously allocated blocks   //
// are ready to be reused.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::restart()
{
  uintptr_t alignptr;

  items = 0;
  maxitems = 0;

  // Set the currently active block.
  nowblock = firstblock;
  // Find the first item in the pool.  Increment by the size of (void *).
  alignptr = (uintptr_t) (nowblock + 1);
  // Align the item on an `alignbytes'-byte boundary.
  nextitem = (void *)
    (alignptr + (uintptr_t) alignbytes -
     (alignptr % (uintptr_t) alignbytes));
  // There are lots of unallocated items left in this block.
  unallocateditems = itemsperblock;
  // The stack of deallocated items is empty.
  deaditemstack = (void *) nullptr;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// alloc()   Allocate space for an item.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::memorypool::alloc()
{
  void *newitem;
  void **newblock;
  uintptr_t alignptr;

  // First check the linked list of dead items.  If the list is not 
  //   empty, allocate an item from the list rather than a fresh one.
  if (deaditemstack != (void *) nullptr) {
    newitem = deaditemstack;                     // Take first item in list.
    deaditemstack = * (void **) deaditemstack;
  } else {
    // Check if there are any free items left in the current block.
    if (unallocateditems == 0) {
      // Check if another block must be allocated.
      if (*nowblock == (void *) nullptr) {
        // Allocate a new block of items, pointed to by the previous block.
        newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) 
                                    + alignbytes);
        if (newblock == (void **) nullptr) {
          terminatetetgen(1);
        }
        *nowblock = (void *) newblock;
        // The next block pointer is nullptr.
        *newblock = (void *) nullptr;
      }
      // Move to the new block.
      nowblock = (void **) *nowblock;
      // Find the first item in the block.
      //   Increment by the size of (void *).
      alignptr = (uintptr_t) (nowblock + 1);
      // Align the item on an `alignbytes'-byte boundary.
      nextitem = (void *)
        (alignptr + (uintptr_t) alignbytes -
         (alignptr % (uintptr_t) alignbytes));
      // There are lots of unallocated items left in this block.
      unallocateditems = itemsperblock;
    }
    // Allocate a new item.
    newitem = nextitem;
    // Advance `nextitem' pointer to next free item in block.
    nextitem = (void *) ((uintptr_t) nextitem + itembytes);
    unallocateditems--;
    maxitems++;
  }
  items++;
  return newitem;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dealloc()   Deallocate space for an item.                                 //
//                                                                           //
// The deallocated space is stored in a queue for later reuse.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::dealloc(void *dyingitem)
{
  // Push freshly killed item onto stack.
  *((void **) dyingitem) = deaditemstack;
  deaditemstack = dyingitem;
  items--;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// traversalinit()   Prepare to traverse the entire list of items.           //
//                                                                           //
// This routine is used in conjunction with traverse().                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::traversalinit()
{
  uintptr_t alignptr;

  // Begin the traversal in the first block.
  pathblock = firstblock;
  // Find the first item in the block.  Increment by the size of (void *).
  alignptr = (uintptr_t) (pathblock + 1);
  // Align with item on an `alignbytes'-byte boundary.
  pathitem = (void *)
    (alignptr + (uintptr_t) alignbytes -
     (alignptr % (uintptr_t) alignbytes));
  // Set the number of items left in the current block.
  pathitemsleft = itemsperblock;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// traverse()   Find the next item in the list.                              //
//                                                                           //
// This routine is used in conjunction with traversalinit().  Be forewarned  //
// that this routine successively returns all items in the list, including   //
// deallocated ones on the deaditemqueue. It's up to you to figure out which //
// ones are actually dead.  It can usually be done more space-efficiently by //
// a routine that knows something about the structure of the item.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::memorypool::traverse()
{
  void *newitem;
  uintptr_t alignptr;

  // Stop upon exhausting the list of items.
  if (pathitem == nextitem) {
    return (void *) nullptr;
  }
  // Check whether any untraversed items remain in the current block.
  if (pathitemsleft == 0) {
    // Find the next block.
    pathblock = (void **) *pathblock;
    // Find the first item in the block.  Increment by the size of (void *).
    alignptr = (uintptr_t) (pathblock + 1);
    // Align with item on an `alignbytes'-byte boundary.
    pathitem = (void *)
      (alignptr + (uintptr_t) alignbytes -
       (alignptr % (uintptr_t) alignbytes));
    // Set the number of items left in the current block.
    pathitemsleft = itemsperblock;
  }
  newitem = pathitem;
  // Find the next item in the block.
  pathitem = (void *) ((uintptr_t) pathitem + itembytes);
  pathitemsleft--;
  return newitem;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makeindex2pointmap()    Create a map from index to vertices.              //
//                                                                           //
// 'idx2verlist' returns the created map.  Traverse all vertices, a pointer  //
// to each vertex is set into the array.  The pointer to the first vertex is //
// saved in 'idx2verlist[in->firstnumber]'.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
{
  point pointloop;
  int idx;

  if (b->verbose > 1) {
    printf("  Constructing mapping from indices to points.\n");
  }

  idx2verlist = new point[points->items + 1];

  points->traversalinit();
  pointloop = pointtraverse();
  idx =  in->firstnumber;
  while (pointloop != (point) nullptr) {
    idx2verlist[idx++] = pointloop;
    pointloop = pointtraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makesubfacemap()    Create a map from vertex to subfaces incident at it.  //
//                                                                           //
// The map is returned in two arrays 'idx2faclist' and 'facperverlist'.  All //
// subfaces incident at i-th vertex (i is counted from 0) are found in the   //
// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1].   //
// Each entry in facperverlist[j] is a subface whose origin is the vertex.   //
//                                                                           //
// NOTE: These two arrays will be created inside this routine, don't forget  //
// to free them after using.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist,
                                  face*& facperverlist)
{
  face shloop;
  int i, j, k;

  if (b->verbose > 1) {
    printf("  Making a map from points to subfaces.\n");
  }

  // Initialize 'idx2faclist'.
  idx2faclist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0;

  // Loop all subfaces, counter the number of subfaces incident at a vertex.
  pool->traversalinit();
  shloop.sh = shellfacetraverse(pool);
  while (shloop.sh != (shellface *) nullptr) {
    // Increment the number of incident subfaces for each vertex.
    j = pointmark((point) shloop.sh[3]) - in->firstnumber;
    idx2faclist[j]++;
    j = pointmark((point) shloop.sh[4]) - in->firstnumber;
    idx2faclist[j]++;
    // Skip the third corner if it is a segment.
    if (shloop.sh[5] != nullptr) {
      j = pointmark((point) shloop.sh[5]) - in->firstnumber;
      idx2faclist[j]++;
    }
    shloop.sh = shellfacetraverse(pool);
  }

  // Calculate the total length of array 'facperverlist'.
  j = idx2faclist[0];
  idx2faclist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2faclist[i + 1];
    idx2faclist[i + 1] = idx2faclist[i] + j;
    j = k;
  }

  // The total length is in the last unit of idx2faclist.
  facperverlist = new face[idx2faclist[i]];

  // Loop all subfaces again, remember the subfaces at each vertex.
  pool->traversalinit();
  shloop.sh = shellfacetraverse(pool);
  while (shloop.sh != (shellface *) nullptr) {
    j = pointmark((point) shloop.sh[3]) - in->firstnumber;
    shloop.shver = 0; // save the origin.
    facperverlist[idx2faclist[j]] = shloop;
    idx2faclist[j]++;
    // Is it a subface or a subsegment?
    if (shloop.sh[5] != nullptr) {
      j = pointmark((point) shloop.sh[4]) - in->firstnumber;
      shloop.shver = 2; // save the origin.
      facperverlist[idx2faclist[j]] = shloop;
      idx2faclist[j]++;
      j = pointmark((point) shloop.sh[5]) - in->firstnumber;
      shloop.shver = 4; // save the origin.
      facperverlist[idx2faclist[j]] = shloop;
      idx2faclist[j]++;
    } else {
      j = pointmark((point) shloop.sh[4]) - in->firstnumber;
      shloop.shver = 1; // save the origin.
      facperverlist[idx2faclist[j]] = shloop;
      idx2faclist[j]++;
    }
    shloop.sh = shellfacetraverse(pool);
  }

  // Contents in 'idx2faclist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2faclist[i + 1] = idx2faclist[i];
  }
  idx2faclist[0] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedrondealloc()    Deallocate space for a tet., marking it dead.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron)
{
  // Set tetrahedron's vertices to nullptr. This makes it possible to detect
  //   dead tetrahedra when traversing the list of all tetrahedra.
  dyingtetrahedron[4] = (tetrahedron) nullptr;

  // Dealloc the space to subfaces/subsegments.
  if (dyingtetrahedron[8] != nullptr) {
    tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
  }
  if (dyingtetrahedron[9] != nullptr) {
    tet2subpool->dealloc((shellface *) dyingtetrahedron[9]);
  }

  tetrahedrons->dealloc((void *) dyingtetrahedron);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedrontraverse()    Traverse the tetrahedra, skipping dead ones.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse()
{
  tetrahedron *newtetrahedron;

  do {
    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
    if (newtetrahedron == (tetrahedron *) nullptr) {
      return (tetrahedron *) nullptr;
    }
  } while ((newtetrahedron[4] == (tetrahedron) nullptr) ||
           ((point) newtetrahedron[7] == dummypoint));
  return newtetrahedron;
}

tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse()
{
  tetrahedron *newtetrahedron;

  do {
    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
    if (newtetrahedron == (tetrahedron *) nullptr) {
      return (tetrahedron *) nullptr;
    }
  } while (newtetrahedron[4] == (tetrahedron) nullptr); // Skip dead ones.
  return newtetrahedron;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shellfacedealloc()    Deallocate space for a shellface, marking it dead.  //
//                       Used both for dealloc a subface and subsegment.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh)
{
  // Set shellface's vertices to nullptr. This makes it possible to detect dead
  //   shellfaces when traversing the list of all shellfaces.
  dyingsh[3] = (shellface) nullptr;
  pool->dealloc((void *) dyingsh);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shellfacetraverse()    Traverse the subfaces, skipping dead ones. Used    //
//                        for both subfaces and subsegments pool traverse.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool)
{
  shellface *newshellface;

  do {
    newshellface = (shellface *) pool->traverse();
    if (newshellface == (shellface *) nullptr) {
      return (shellface *) nullptr;
    }
  } while (newshellface[3] == (shellface) nullptr);          // Skip dead ones.
  return newshellface;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// pointdealloc()    Deallocate space for a point, marking it dead.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::pointdealloc(point dyingpoint)
{
  // Mark the point as dead. This  makes it possible to detect dead points
  //   when traversing the list of all points.
  setpointtype(dyingpoint, DEADVERTEX);
  points->dealloc((void *) dyingpoint);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// pointtraverse()    Traverse the points, skipping dead ones.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::pointtraverse()
{
  point newpoint;

  do {
    newpoint = (point) points->traverse();
    if (newpoint == (point) nullptr) {
      return (point) nullptr;
    }
  } while (pointtype(newpoint) == DEADVERTEX);            // Skip dead ones.
  return newpoint;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// maketetrahedron()    Create a new tetrahedron.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::maketetrahedron(triface *newtet)
{
  newtet->tet = (tetrahedron *) tetrahedrons->alloc();

  // Initialize the four adjoining tetrahedra to be "outer space".
  newtet->tet[0] = nullptr;
  newtet->tet[1] = nullptr;
  newtet->tet[2] = nullptr;
  newtet->tet[3] = nullptr;
  // Four nullptr vertices.
  newtet->tet[4] = nullptr;
  newtet->tet[5] = nullptr;
  newtet->tet[6] = nullptr;
  newtet->tet[7] = nullptr;
  // No attached segments and sbfaces yet.
  newtet->tet[8] = nullptr; 
  newtet->tet[9] = nullptr; 
  // Initialize the marker (clear all flags).
  setelemmarker(newtet->tet, 0);
  for (int i = 0; i < numelemattrib; i++) {
    setelemattribute(newtet->tet, i, 0.0);
  }
  if (b->varvolume) {
    setvolumebound(newtet->tet, -1.0);
  }

  // Initialize the version to be Zero.
  newtet->ver = 11;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makeshellface()    Create a new shellface with version zero. Used for     //
//                    both subfaces and seusegments.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makeshellface(memorypool *pool, face *newface)
{
  newface->sh = (shellface *) pool->alloc();

  // No adjointing subfaces.
  newface->sh[0] = nullptr;
  newface->sh[1] = nullptr;
  newface->sh[2] = nullptr;
  // Three nullptr vertices.
  newface->sh[3] = nullptr;
  newface->sh[4] = nullptr;
  newface->sh[5] = nullptr;
  // No adjoining subsegments.
  newface->sh[6] = nullptr;
  newface->sh[7] = nullptr;
  newface->sh[8] = nullptr;
  // No adjoining tetrahedra.
  newface->sh[9] = nullptr;
  newface->sh[10] = nullptr;
  if (checkconstraints) {
    // Initialize the maximum area bound.
    setareabound(*newface, 0.0);
  }

  // Clear the infection and marktest bits.
  ((int *) (newface->sh))[shmarkindex + 1] = 0;
  
  // Set the boundary marker to zero.
  setshellmark(*newface, 0);
  // Set the default face type.
  setshelltype(*newface, NSHARP);

  newface->shver = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makepoint()    Create a new point.                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype)
{
  int i;

  *pnewpoint = (point) points->alloc();

  // Initialize the point attributes.
  for (i = 0; i < numpointattrib; i++) {
    (*pnewpoint)[3 + i] = 0.0;
  }
  // Initialize the metric tensor.
  for (i = 0; i < sizeoftensor; i++) {
    (*pnewpoint)[pointmtrindex + i] = 0.0;
  }
  setpoint2tet(*pnewpoint, nullptr);
  setpoint2ppt(*pnewpoint, nullptr);
  if (b->plc || b->psc || b->refine) {
    // Initialize the point-to-simplex field.
    setpoint2sh(*pnewpoint, nullptr);
    if (b->metric && (bgm != nullptr)) {
      setpoint2bgmtet(*pnewpoint, nullptr);
    }
  }
  // Initialize the point marker (starting from in->firstnumber).
  setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber));
  // Clear all flags.
  ((int *) (*pnewpoint))[pointmarkindex + 1] = 0;
  // Initialize (set) the point type. 
  setpointtype(*pnewpoint, vtype);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initializepools()    Calculate the sizes of the point, tetrahedron, and   //
//                      subface. Initialize their memory pools.              //
//                                                                           //
// This routine also computes the indices 'pointmarkindex', 'point2simindex',//
// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'.  They are  //
// used to find values within each point and tetrahedron, respectively.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initializepools()
{
  int pointsize = 0, elesize = 0, shsize = 0;
  int i;

  if (b->verbose) {
    printf("  Initializing memorypools.\n");
    printf("  tetrahedron per block: %d.\n", b->tetrahedraperblock);
  }

  inittables();

  // There are three input point lists available, which are in, addin,
  //   and bgm->in. These point lists may have different number of 
  //   attributes. Decide the maximum number.
  numpointattrib = in->numberofpointattributes;
  if (bgm != nullptr) {
    if (bgm->in->numberofpointattributes > numpointattrib) {
      numpointattrib = bgm->in->numberofpointattributes;
    }
  }
  if (addin != nullptr) {
    if (addin->numberofpointattributes > numpointattrib) {
      numpointattrib = addin->numberofpointattributes;
    }
  }
  if (b->weighted || b->flipinsert) { // -w or -L.
    // The internal number of point attribute needs to be at least 1
    //   (for storing point weights).
    if (numpointattrib == 0) {    
      numpointattrib = 1;
    }
  }

  // Default varconstraint = 0;
  if (in->segmentconstraintlist || in->facetconstraintlist) {
    checkconstraints = 1;
  }

  // The index within each point at which its metric tensor is found. 
  // Each vertex has three coordinates.
  if (b->psc) {
    // '-s' option (PSC), the u,v coordinates are provided.
    pointmtrindex = 5 + numpointattrib;
  } else {
    pointmtrindex = 3 + numpointattrib;
  }
  // The index within each point at which its u, v coordinates are found.
  pointparamindex = 3 + (numpointattrib > 0);
  // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
  if (b->metric) {
    // Decide the size (1, 3, or 6) of the metric tensor.
    if (bgm != (tetgenmesh *) nullptr) {
      // A background mesh is allocated. It may not exist though.
      sizeoftensor = (bgm->in != (tetgenio *) nullptr) ? 
        bgm->in->numberofpointmtrs : in->numberofpointmtrs;
    } else {
      // No given background mesh - Itself is a background mesh.
      sizeoftensor = in->numberofpointmtrs;
    }
    // Make sure sizeoftensor is at least 1.
    sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1;
  } else {
    // For '-q' option. Make sure to have space for saving a scalar value.
    sizeoftensor = b->quality ? 1 : 0;
  }
  // The index within each point at which an element pointer is found, where
  //   the index is measured in pointers. Ensure the index is aligned to a
  //   sizeof(tetrahedron)-byte address.
  point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL)
                 + sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
  // [Bruno Levy] seems to fix the problem for computing
  // Delaunay triangulation from 3D points alone.
  if (true || b->plc || b->refine || b->voroout) {
    // Increase the point size by three pointers, which are:
    //   - a pointer to a tet, read by point2tet();
    //   - a pointer to a parent point, read by point2ppt()).
    //   - a pointer to a subface or segment, read by point2sh();
    if (b->metric && (bgm != (tetgenmesh *) nullptr)) {
      // Increase one pointer into the background mesh, point2bgmtet().
      pointsize = (point2simindex + 4) * sizeof(tetrahedron);
    } else {
      pointsize = (point2simindex + 3) * sizeof(tetrahedron);
    }
  } else {
    // Increase the point size by two pointer, which are:
    //   - a pointer to a tet, read by point2tet();
    //   - a pointer to a parent point, read by point2ppt()). -- Used by btree.
    pointsize = (point2simindex + 2) * sizeof(tetrahedron);
  }
  // The index within each point at which the boundary marker is found,
  //   Ensure the point marker is aligned to a sizeof(int)-byte address.
  pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
  // Now point size is the ints (inidcated by pointmarkindex) plus:
  //   - an integer for boundary marker;
  //   - an integer for vertex type;
  //   - an integer for geometry tag (optional, -s option).
  pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron);

  // Initialize the pool of vertices.
  points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0);

  if (b->verbose) {
    printf("  Size of a point: %d bytes.\n", points->itembytes);
  }

  // Initialize the infinite vertex.
  dummypoint = (point) new char[pointsize];
  // Initialize all fields of this point.
  dummypoint[0] = 0.0;
  dummypoint[1] = 0.0;
  dummypoint[2] = 0.0;
  for (i = 0; i < numpointattrib; i++) {
    dummypoint[3 + i] = 0.0;
  }
  // Initialize the metric tensor.
  for (i = 0; i < sizeoftensor; i++) {
    dummypoint[pointmtrindex + i] = 0.0;
  }
  setpoint2tet(dummypoint, nullptr);
  setpoint2ppt(dummypoint, nullptr);
  if (b->plc || b->psc || b->refine) {
    // Initialize the point-to-simplex field.
    setpoint2sh(dummypoint, nullptr);
    if (b->metric && (bgm != nullptr)) {
      setpoint2bgmtet(dummypoint, nullptr);
    }
  }
  // Initialize the point marker (starting from in->firstnumber).
  setpointmark(dummypoint, -1); // The unique marker for dummypoint.
  // Clear all flags.
  ((int *) (dummypoint))[pointmarkindex + 1] = 0;
  // Initialize (set) the point type. 
  setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter.

  // The number of bytes occupied by a tetrahedron is varying by the user-
  //   specified options. The contents of the first 12 pointers are listed
  //   in the following table:
  //     [0]  |__ neighbor at f0 __|
  //     [1]  |__ neighbor at f1 __|
  //     [2]  |__ neighbor at f2 __|
  //     [3]  |__ neighbor at f3 __|
  //     [4]  |_____ vertex p0 ____|
  //     [5]  |_____ vertex p1 ____|
  //     [6]  |_____ vertex p2 ____|
  //     [7]  |_____ vertex p3 ____|
  //     [8]  |__ segments array __| (used by -p)
  //     [9]  |__ subfaces array __| (used by -p)
  //    [10]  |_____ reserved _____|
  //    [11]  |___ elem marker ____| (used as an integer)

  elesize = 12 * sizeof(tetrahedron); 

  // The index to find the element markers. An integer containing varies
  //   flags and element counter. 
  assert(sizeof(int) <= sizeof(tetrahedron));
  assert((sizeof(tetrahedron) % sizeof(int)) == 0);
  elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int);

  // The actual number of element attributes. Note that if the
  //   `b->regionattrib' flag is set, an additional attribute will be added.
  numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0);

  // The index within each element at which its attributes are found, where
  //   the index is measured in REALs. 
  elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
  // The index within each element at which the maximum voulme bound is
  //   found, where the index is measured in REALs.
  volumeboundindex = elemattribindex + numelemattrib;
  // If element attributes or an constraint are needed, increase the number
  //   of bytes occupied by an element.
  if (b->varvolume) {
    elesize = (volumeboundindex + 1) * sizeof(REAL);
  } else if (numelemattrib > 0) {
    elesize = volumeboundindex * sizeof(REAL);
  }


  // Having determined the memory size of an element, initialize the pool.
  tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *),
                                16);

  if (b->verbose) {
    printf("  Size of a tetrahedron: %d (%d) bytes.\n", elesize,
           tetrahedrons->itembytes);
  }

  if (b->plc || b->refine) { // if (b->useshelles) {
    // The number of bytes occupied by a subface.  The list of pointers
    //   stored in a subface are: three to other subfaces, three to corners,
    //   three to subsegments, two to tetrahedra.
    shsize = 11 * sizeof(shellface);
    // The index within each subface at which the maximum area bound is
    //   found, where the index is measured in REALs.
    areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL);
    // If -q switch is in use, increase the number of bytes occupied by
    //   a subface for saving maximum area bound.
    if (checkconstraints) { 
      shsize = (areaboundindex + 1) * sizeof(REAL);
    } else {
      shsize = areaboundindex * sizeof(REAL);
    }
    // The index within subface at which the facet marker is found. Ensure
    //   the marker is aligned to a sizeof(int)-byte address.
    shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int);
    // Increase the number of bytes by two or three integers, one for facet
    //   marker, one for shellface type, and optionally one for pbc group.
    shsize = (shmarkindex + 2) * sizeof(shellface);

    // Initialize the pool of subfaces. Each subface record is eight-byte
    //   aligned so it has room to store an edge version (from 0 to 5) in
    //   the least three bits.
    subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);

    if (b->verbose) {
      printf("  Size of a shellface: %d (%d) bytes.\n", shsize,
             subfaces->itembytes);
    }

    // Initialize the pool of subsegments. The subsegment's record is same
    //   with subface.
    subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);

    // Initialize the pool for tet-subseg connections.
    tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, 
                                 sizeof(void *), 0);
    // Initialize the pool for tet-subface connections.
    tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, 
                                 sizeof(void *), 0);

    // Initialize arraypools for segment & facet recovery.
    subsegstack = new arraypool(sizeof(face), 10);
    subfacstack = new arraypool(sizeof(face), 10);
    subvertstack = new arraypool(sizeof(point), 8);

    // Initialize arraypools for surface point insertion/deletion.
    caveshlist = new arraypool(sizeof(face), 8);
    caveshbdlist = new arraypool(sizeof(face), 8);
    cavesegshlist = new arraypool(sizeof(face), 4);

    cavetetshlist = new arraypool(sizeof(face), 8);
    cavetetseglist = new arraypool(sizeof(face), 8);
    caveencshlist = new arraypool(sizeof(face), 8);
    caveencseglist = new arraypool(sizeof(face), 8);
  }

  // Initialize the pools for flips.
  flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0);
  unflipqueue = new arraypool(sizeof(badface), 10);

  // Initialize the arraypools for point insertion.
  cavetetlist = new arraypool(sizeof(triface), 10);
  cavebdrylist = new arraypool(sizeof(triface), 10);
  caveoldtetlist = new arraypool(sizeof(triface), 10);
  cavetetvertlist = new arraypool(sizeof(point), 10);
}

////                                                                       ////
////                                                                       ////
//// mempool_cxx //////////////////////////////////////////////////////////////

//// geom_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

// PI is the ratio of a circle's circumference to its diameter.
REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582;


#ifdef USE_PCK //[Bruno]

} // close namespace GEO_3rdParty
    
// Prototypes for PCK predicates
namespace GEO {
    
    namespace PCK {

        Sign GEOGRAM_API side4_3d(
            const double* p0, const double* p1, 
            const double* p2, const double* p3, const double* p4
        );

        Sign GEOGRAM_API side4_3d_SOS(
            const double* p0, const double* p1, 
            const double* p2, const double* p3, const double* p4
        );

        Sign GEOGRAM_API orient_3d(
            const double* p0, const double* p1,
            const double* p2, const double* p3
        );

        Sign GEOGRAM_API orient_3dlifted(
            const double* p0, const double* p1,
            const double* p2, const double* p3, const double* p4,
            double h0, double h1, double h2, double h3, double h4
        );

        Sign GEOGRAM_API orient_3dlifted_SOS(
            const double* p0, const double* p1,
            const double* p2, const double* p3, const double* p4,
            double h0, double h1, double h2, double h3, double h4
        );

        void GEOGRAM_API initialize();
    }
}

namespace GEO_3rdParty { // re-open namespace GEO_3rdParty

// Wrapper around PCK predicates to make them sourcelevel
// compatible with Shewchuk's predicates.
namespace {

    REAL orient3d(REAL* pa, REAL *pb, REAL *pc, REAL *pd) {
        // Warning: PCK seems to have a convention 
        // different from Shewchuk's, to be checked
        // (and also for other predicates...)
        // If I do not change the sign, I do not obtain
        // the same number of tets in BDEL and tetgen.
        return -REAL(GEO::PCK::orient_3d(pa,pb,pc,pd));
    }
    
    REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) {
        return REAL(GEO::PCK::side4_3d(pa,pb,pc,pd,pe));
    }

    REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
                  REAL ah, REAL bh, REAL ch, REAL dh, REAL eh) {
        return REAL(GEO::PCK::orient_3dlifted(pa,pb,pc,pd,pe,ah,bh,ch,dh,eh));
    }

    /**
     * \brief Computes a four-by-four determinant.
     */
    inline REAL det4x4(
        REAL a11, REAL a12, REAL a13, REAL a14,
        REAL a21, REAL a22, REAL a23, REAL a24,               
        REAL a31, REAL a32, REAL a33, REAL a34,  
        REAL a41, REAL a42, REAL a43, REAL a44  
    ) {
        REAL m12 = a21*a12 - a11*a22;
        REAL m13 = a31*a12 - a11*a32;
        REAL m14 = a41*a12 - a11*a42;
        REAL m23 = a31*a22 - a21*a32;
        REAL m24 = a41*a22 - a21*a42;
        REAL m34 = a41*a32 - a31*a42;

        REAL m123 = m23*a13 - m13*a23 + m12*a33;
        REAL m124 = m24*a13 - m14*a23 + m12*a43;
        REAL m134 = m34*a13 - m14*a33 + m13*a43;
        REAL m234 = m34*a23 - m24*a33 + m23*a43;
        
        return (m234*a14 - m134*a24 + m124*a34 - m123*a44);
    }   

    // PCK predicates only return the sign of the predicate,
    // whereas Shewchuk ones also return a real number that
    // has the approximate value of the determinant computed
    // by the predicates (and of course its exact sign).
    // Some parts of tetgen need also to compute the value
    // of the determinant, so I added this function.
    
    REAL orient4d_numeric(
        REAL *p0, REAL *p1, REAL *p2, REAL *p3, REAL *p4,
        REAL h0, REAL h1, REAL h2, REAL h3, REAL h4
    ) {
       
        REAL a11 = p1[0] - p0[0] ;
        REAL a12 = p1[1] - p0[1] ;
        REAL a13 = p1[2] - p0[2] ;
        REAL a14 = h1 - h0 ;

        REAL a21 = p2[0] - p0[0] ;
        REAL a22 = p2[1] - p0[1] ;
        REAL a23 = p2[2] - p0[2] ;
        REAL a24 = h2 - h0 ;
        
        REAL a31 = p3[0] - p0[0] ;
        REAL a32 = p3[1] - p0[1] ;
        REAL a33 = p3[2] - p0[2] ;
        REAL a34 = h3 - h0 ;
        
        REAL a41 = p4[0] - p0[0] ;
        REAL a42 = p4[1] - p0[1] ;
        REAL a43 = p4[2] - p0[2] ;
        REAL a44 = h4 - h0 ;
        
        REAL Delta = det4x4(
            a11,a12,a13,a14,
            a21,a22,a23,a24,
            a31,a32,a33,a34,
            a41,a42,a43,a44
         ) ;
        return Delta;
    }    
    

    void exactinit() {
        GEO::PCK::initialize();
    }
}

#else 

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Robust Geometric predicates                                               //
//                                                                           //
// Geometric predicates are simple tests of spatial relations of a set of d- //
// dimensional points, such as the orientation test and the point-in-sphere  //
// test. Each of these tests is performed by evaluating the sign of a deter- //
// minant of a matrix whose entries are the coordinates of these points.  If //
// the computation is performed by using the floating-point numbers, e.g.,   //
// the single or double precision numbers in C/C++, roundoff error may cause //
// an incorrect result. This may either lead to a wrong result or eventually //
// lead to a failure of the program.  Computing the predicates exactly will  //
// avoid the error and make the program robust.                              //
//                                                                           //
// The following routines are the robust geometric predicates for 3D orient- //
// ation test and point-in-sphere test.  They were implemented by Shewchuk.  //
// The source code are generously provided by him in the public domain,      //
// http://www.cs.cmu.edu/~quake/robust.html. predicates.cxx is a C++ version //
// of the original C code.                                                   //
//                                                                           //
// The original predicates of Shewchuk only use "dynamic filters", i.e., it  //
// computes the error at run time step by step. TetGen first adds a "static  //
// filter" in each predicate. It estimates the maximal possible error in all //
// cases.  So it can safely and quickly answer many easy cases.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL exactinit() ;
REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe);
REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
              REAL ah, REAL bh, REAL ch, REAL dh, REAL eh);

inline REAL orient4d_numeric(
    REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
    REAL ah, REAL bh, REAL ch, REAL dh, REAL eh) {
    return orient4d(pa,pb,pc,pd,pe,ah,bh,ch,dh,eh);
}     


#endif

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insphere_s()    Insphere test with symbolic perturbation.                 //
//                                                                           //
// Given four points pa, pb, pc, and pd, test if the point pe lies inside or //
// outside the circumscirbed sphere of the four points.                      //
//                                                                           //
// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
// points pa, pb, and pc. Otherwise, the returned sign is flipped.           //
//                                                                           //
// Return a positive value (> 0) if pe lies inside, a negative value (< 0)   //
// if pe lies outside the sphere, the returned value will not be zero.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe)
{
#ifdef USE_PCK // [Bruno]
    return REAL(GEO::PCK::side4_3d_SOS(pa,pb,pc,pd,pe));
#else
  REAL sign;

  sign = insphere(pa, pb, pc, pd, pe);
  if (sign != 0.0) {
    return sign;
  }

  // Symbolic perturbation.
  point pt[5], swappt;
  REAL oriA, oriB;
  int swaps, count;
  int n, i;

  pt[0] = pa;
  pt[1] = pb;
  pt[2] = pc;
  pt[3] = pd;
  pt[4] = pe;
  
  // Sort the five points such that their indices are in the increasing
  //   order. An optimized bubble sort algorithm is used, i.e., it has
  //   the worst case O(n^2) runtime, but it is usually much faster.
  swaps = 0; // Record the total number of swaps.
  n = 5;
  do {
    count = 0;
    n = n - 1;
    for (i = 0; i < n; i++) {
      if (pointmark(pt[i]) > pointmark(pt[i+1])) {
        swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
        count++;
      }
    }
    swaps += count;
  } while (count > 0); // Continue if some points are swapped.

  oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
  if (oriA != 0.0) {
    // Flip the sign if there are odd number of swaps.
    if ((swaps % 2) != 0) oriA = -oriA;
    return oriA;
  }
  
  oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
  assert(oriB != 0.0); // SELF_CHECK
  // Flip the sign if there are odd number of swaps.
  if ((swaps % 2) != 0) oriB = -oriB;
  return oriB;
#endif
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// orient4d_s()    4d orientation test with symbolic perturbation.           //
//                                                                           //
// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted //
// point pe' in R^4 lies below or above the hyperplance passing through the  //
// four points pa', pb', pc', and pd'.                                       //
//                                                                           //
// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, //
// pd} is positive (NOT zero), i.e., pd lies above the plane passing through //
// the points pa, pb, and pc. Otherwise, the returned sign is flipped.       //
//                                                                           //
// Return a positive value (> 0) if pe' lies below, a negative value (< 0)   //
// if pe' lies above the hyperplane, the returned value should not be zero.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
                            REAL aheight, REAL bheight, REAL cheight, 
                            REAL dheight, REAL eheight) {
#ifdef USE_PCK // [Bruno]

    return REAL(GEO::PCK::orient_3dlifted_SOS(
                    pa,pb,pc,pd,pe,aheight,bheight,cheight,dheight,eheight
    ));

#else

  REAL sign;

  sign = orient4d(pa, pb, pc, pd, pe, 
                  aheight, bheight, cheight, dheight, eheight);
  if (sign != 0.0) {
    return sign;
  }

  // Symbolic perturbation.
  point pt[5], swappt;
  REAL oriA, oriB;
  int swaps, count;
  int n, i;

  pt[0] = pa;
  pt[1] = pb;
  pt[2] = pc;
  pt[3] = pd;
  pt[4] = pe;
  
  // Sort the five points such that their indices are in the increasing
  //   order. An optimized bubble sort algorithm is used, i.e., it has
  //   the worst case O(n^2) runtime, but it is usually much faster.
  swaps = 0; // Record the total number of swaps.
  n = 5;
  do {
    count = 0;
    n = n - 1;
    for (i = 0; i < n; i++) {
      if (pointmark(pt[i]) > pointmark(pt[i+1])) {
        swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
        count++;
      }
    }
    swaps += count;
  } while (count > 0); // Continue if some points are swapped.

  oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
  if (oriA != 0.0) {
    // Flip the sign if there are odd number of swaps.
    if ((swaps % 2) != 0) oriA = -oriA;
    return oriA;
  }
  
  oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
  assert(oriB != 0.0); // SELF_CHECK
  // Flip the sign if there are odd number of swaps.
  if ((swaps % 2) != 0) oriB = -oriB;
  return oriB;

#endif
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_test()    Triangle-edge intersection test.                       //
//                                                                           //
// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, //
// Q) in 3D, and tests if they intersect each other.                         //
//                                                                           //
// If the point 'R' is not nullptr, it lies strictly above the plane defined by //
// A, B, C. It is used in test when T and E are coplanar.                    //
//                                                                           //
// If T and E intersect each other, they may intersect in different ways. If //
// 'level' > 0, their intersection type will be reported 'types' and 'pos'.  //
//                                                                           //
// The retrun value indicates one of the following cases:                    //
//   - 0, T and E are disjoint.                                              //
//   - 1, T and E intersect each other.                                      //
//   - 2, T and E are not coplanar. They intersect at a single point.        //
//   - 4, T and E are coplanar. They intersect at a single point or a line   //
//        segment (if types[1] != DISJOINT).                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2)

#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp)

int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, 
                            point R, int level, int *types, int *pos)
{
  point U[3], V[3];  // The permuted vectors of points.
  int pu[3], pv[3];  // The original positions of points.
  REAL abovept[3];
  REAL sA, sB, sC;
  REAL s1, s2, s3, s4;
  int z1;

  if (R == nullptr) {
    // Calculate a lift point.
    if (1) {
      REAL n[3], len;
      // Calculate a lift point, saved in dummypoint.
      facenormal(A, B, C, n, 1, nullptr);
      len = sqrt(dot(n, n));
      if (len != 0) {
        n[0] /= len;
        n[1] /= len;
        n[2] /= len;
        len = distance(A, B);
        len += distance(B, C);
        len += distance(C, A);
        len /= 3.0;
        R = abovept; //dummypoint;
        R[0] = A[0] + len * n[0];
        R[1] = A[1] + len * n[1];
        R[2] = A[2] + len * n[2];
      } else {
        // The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close)
        //   to a line.  We need a line-line intersection test.
        //assert(0);
        // !!! A non-save return value.!!!
        return 0;  // DISJOINT
      }
    }
  }

  // Test A's, B's, and C's orientations wrt plane PQR. 
  sA = orient3d(P, Q, R, A);
  sB = orient3d(P, Q, R, B);
  sC = orient3d(P, Q, R, C);


  if (sA < 0) {
    if (sB < 0) {
      if (sC < 0) { // (---).
        return 0; 
      } else {
        if (sC > 0) { // (--+).
          // All points are in the right positions.
          SETVECTOR3(U, A, B, C);  // I3
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 0, 1, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else { // (--0).
          SETVECTOR3(U, A, B, C);  // I3
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 0, 1, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        }
      }
    } else { 
      if (sB > 0) {
        if (sC < 0) { // (-+-).
          SETVECTOR3(U, C, A, B);  // PT = ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 2, 0, 1);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else {
          if (sC > 0) { // (-++).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else { // (-+0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          }
        }
      } else {
        if (sC < 0) { // (-0-).
          SETVECTOR3(U, C, A, B);  // PT = ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 2, 0, 1);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        } else {
          if (sC > 0) { // (-0+).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          } else { // (-00).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          }
        }
      }
    }
  } else {
    if (sA > 0) {
      if (sB < 0) {
        if (sC < 0) { // (+--).
          SETVECTOR3(U, B, C, A);  // PT = ST x ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 1, 2, 0);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else {
          if (sC > 0) { // (+-+).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else { // (+-0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          }
        }
      } else { 
        if (sB > 0) {
          if (sC < 0) { // (++-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else {
            if (sC > 0) { // (+++).
              return 0; 
            } else { // (++0).
              SETVECTOR3(U, A, B, C);  // I3
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 0, 1, 2);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1; 
            }
          }
        } else { // (+0#)
          if (sC < 0) { // (+0-).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          } else {
            if (sC > 0) { // (+0+).
              SETVECTOR3(U, C, A, B);  // PT = ST
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 2, 0, 1);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1;
            } else { // (+00).
              SETVECTOR3(U, B, C, A);  // PT = ST x ST
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 1, 2, 0);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            }
          }
        }
      }
    } else { 
      if (sB < 0) {
        if (sC < 0) { // (0--).
          SETVECTOR3(U, B, C, A);  // PT = ST x ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 1, 2, 0);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        } else {
          if (sC > 0) { // (0-+).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          } else { // (0-0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          }
        }
      } else { 
        if (sB > 0) {
          if (sC < 0) { // (0+-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          } else {
            if (sC > 0) { // (0++).
              SETVECTOR3(U, B, C, A);  // PT = ST x ST
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 1, 2, 0);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1;
            } else { // (0+0).
              SETVECTOR3(U, C, A, B);  // PT = ST
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 2, 0, 1);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            }
          }
        } else { // (00#)
          if (sC < 0) { // (00-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          } else {
            if (sC > 0) { // (00+).
              SETVECTOR3(U, A, B, C);  // I3
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 0, 1, 2);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            } else { // (000)
              // Not possible unless ABC is degenerate.
              // Avoiding compiler warnings.
              SETVECTOR3(U, A, B, C);  // I3
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 0, 1, 2);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 4;
            }
          }
        }
      }
    }
  }

  s1 = orient3d(U[0], U[2], R, V[1]);  // A, C, R, Q
  s2 = orient3d(U[1], U[2], R, V[0]);  // B, C, R, P

  if (s1 > 0) {
    return 0;
  }
  if (s2 < 0) {
    return 0;
  }

  if (level == 0) {
    return 1;  // They are intersected.
  }

  assert(z1 != 4); // SELF_CHECK

  if (z1 == 1) {
    if (s1 == 0) {  // (0###)
      // C = Q.
      types[0] = (int) SHAREVERT;
      pos[0] = pu[2]; // C
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    } else {
      if (s2 == 0) { // (#0##)
        // C = P.
        types[0] = (int) SHAREVERT;
        pos[0] = pu[2]; // C
        pos[1] = pv[0]; // P
        types[1] = (int) DISJOINT;
      } else { // (-+##)
        // C in [P, Q].
        types[0] = (int) ACROSSVERT;
        pos[0] = pu[2]; // C
        pos[1] = pv[0]; // [P, Q]
        types[1] = (int) DISJOINT;
      }
    }
    return 4;
  }

  s3 = orient3d(U[0], U[2], R, V[0]);  // A, C, R, P
  s4 = orient3d(U[1], U[2], R, V[1]);  // B, C, R, Q
      
  if (z1 == 0) {  // (tritri-03)
    if (s1 < 0) {
      if (s3 > 0) {
        assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [k, l] (-+++).
          types[0] = (int) ACROSSEDGE;
          pos[0] = pu[2]; // [C, A]
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHFACE;
          pos[2] = 3;     // [A, B, C]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = l, [P, Q] contains [k, l] (-++0).
            types[0] = (int) ACROSSEDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [k, l] (-++-).
            types[0] = (int) ACROSSEDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) ACROSSEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = k, [P, Q] in [k, l] (-+0+).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHFACE;
            pos[2] = 3;     // [A, B, C]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [k, l] (-+00).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[2]; // [C, A]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[1]; // Q
            } else {
              // P = k, [P, Q] contains [k, l] (-+0-).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[2]; // [C, A]
              pos[1] = pv[0]; // P
              types[1] = (int) ACROSSEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [k, l] (-+-+).
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHFACE;
              pos[2] = 3;     // [A, B, C]
              pos[3] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = l, [P, Q] in [k, l] (-+-0).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[1] = (int) TOUCHEDGE;
                pos[2] = pu[1]; // [B, C]
                pos[3] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [k, l] (-+--).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[1] = (int) ACROSSEDGE;
                pos[2] = pu[1]; // [B, C]
                pos[3] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = l (#0##).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[1]; // [B, C]
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = k (0####)
      types[0] = (int) TOUCHEDGE;
      pos[0] = pu[2]; // [C, A]
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  } else if (z1 == 2) {  // (tritri-23)
    if (s1 < 0) {
      if (s3 > 0) {
        assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [A, l] (-+++).
          types[0] = (int) ACROSSVERT;
          pos[0] = pu[0]; // A
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHFACE;
          pos[2] = 3;     // [A, B, C]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = l, [P, Q] contains [A, l] (-++0).
            types[0] = (int) ACROSSVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [A, l] (-++-).
            types[0] = (int) ACROSSVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) ACROSSEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = A, [P, Q] in [A, l] (-+0+).
            types[0] = (int) SHAREVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHFACE;
            pos[2] = 3;     // [A, B, C]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [A, l] (-+00).
              types[0] = (int) SHAREVERT;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[1]; // Q
            } else { // s4 < 0
              // Q = l, [P, Q] in [A, l] (-+0-).
              types[0] = (int) SHAREVERT;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) ACROSSEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [A, l] (-+-+).
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[0]; // P
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = l, [P, Q] in [A, l] (-+-0).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[1]; // [B, C]
                pos[1] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [A, l] (-+--).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[0] = (int) ACROSSEDGE;
                pos[0] = pu[1]; // [B, C]
                pos[1] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = l (#0##).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[1]; // [B, C]
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = A (0###).
      types[0] = (int) SHAREVERT;
      pos[0] = pu[0]; // A
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  } else if (z1 == 3) {  // (tritri-33)
    if (s1 < 0) {
      if (s3 > 0) {
        assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [A, B] (-+++).
          types[0] = (int) ACROSSVERT;
          pos[0] = pu[0]; // A
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHEDGE;
          pos[2] = pu[0]; // [A, B]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = B, [P, Q] contains [A, B] (-++0).
            types[0] = (int) ACROSSVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) SHAREVERT;
            pos[2] = pu[1]; // B
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [A, B] (-++-).
            types[0] = (int) ACROSSVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) ACROSSVERT;
            pos[2] = pu[1]; // B
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = A, [P, Q] in [A, B] (-+0+).
            types[0] = (int) SHAREVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[0]; // [A, B]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [A, B] (-+00).
              types[0] = (int) SHAREEDGE;
              pos[0] = pu[0]; // [A, B]
              pos[1] = pv[0]; // [P, Q]
              types[1] = (int) DISJOINT;
            } else { // s4 < 0
              // P= A, [P, Q] in [A, B] (-+0-).
              types[0] = (int) SHAREVERT;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) ACROSSVERT;
              pos[2] = pu[1]; // B
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [A, B] (-+-+).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[0]; // [A, B]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[0]; // [A, B]
              pos[3] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = B, [P, Q] in [A, B] (-+-0).
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[0]; // [A, B]
                pos[1] = pv[0]; // P
                types[1] = (int) SHAREVERT;
                pos[2] = pu[1]; // B
                pos[3] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [A, B] (-+--).
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[0]; // [A, B]
                pos[1] = pv[0]; // P
                types[1] = (int) ACROSSVERT;
                pos[2] = pu[1]; // B
                pos[3] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = B (#0##).
            types[0] = (int) SHAREVERT;
            pos[0] = pu[1]; // B
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = A (0###).
      types[0] = (int) SHAREVERT;
      pos[0] = pu[0]; // A
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  }

  return 4;
}

int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R,
                              REAL sP,REAL sQ,int level,int *types,int *pos)
{
  point U[3], V[3]; //, Ptmp;
  int pu[3], pv[3]; //, itmp;
  REAL s1, s2, s3;
  int z1;


  if (sP < 0) {
    if (sQ < 0) { // (--) disjoint
      return 0;
    } else {
      if (sQ > 0) { // (-+)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, P, Q, R);
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 0, 1, 2);
        z1 = 0;
      } else { // (-0)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, P, Q, R);
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 0, 1, 2);
        z1 = 1;
      }
    }
  } else {
    if (sP > 0) { // (+-)
      if (sQ < 0) {
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 1, 0, 2);
        z1 = 0;
      } else {
        if (sQ > 0) { // (++) disjoint
          return 0;
        } else { // (+0)
          SETVECTOR3(U, B, A, C); // A and B are flipped.
          SETVECTOR3(V, P, Q, R);
          SETVECTOR3(pu, 1, 0, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        }
      }
    } else { // sP == 0
      if (sQ < 0) { // (0-)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 1, 0, 2);
        z1 = 1;
      } else {
        if (sQ > 0) { // (0+)
          SETVECTOR3(U, B, A, C);  // A and B are flipped.
          SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
          SETVECTOR3(pu, 1, 0, 2);
          SETVECTOR3(pv, 1, 0, 2);
          z1 = 1;
        } else { // (00)
          // A, B, C, P, and Q are coplanar.
          z1 = 2;
        }
      }
    }
  }

  if (z1 == 2) {
    // The triangle and the edge are coplanar.
    return tri_edge_2d(A, B, C, P, Q, R, level, types, pos);
  }

  s1 = orient3d(U[0], U[1], V[0], V[1]);
  if (s1 < 0) {
    return 0;
  }

  s2 = orient3d(U[1], U[2], V[0], V[1]);
  if (s2 < 0) {
    return 0;
  }

  s3 = orient3d(U[2], U[0], V[0], V[1]);
  if (s3 < 0) {
    return 0;
  }

  if (level == 0) {
    return 1;  // The are intersected.
  }

  types[1] = (int) DISJOINT; // No second intersection point.

  if (z1 == 0) {
    if (s1 > 0) {
      if (s2 > 0) {
        if (s3 > 0) { // (+++)
          // [P, Q] passes interior of [A, B, C].
          types[0] = (int) ACROSSFACE;
          pos[0] = 3;  // interior of [A, B, C]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (++0)
          // [P, Q] intersects [C, A].
          types[0] = (int) ACROSSEDGE;
          pos[0] = pu[2];  // [C, A]
          pos[1] = 0;  // [P, Q]
        }
      } else { // s2 == 0
        if (s3 > 0) { // (+0+)
          // [P, Q] intersects [B, C].
          types[0] = (int) ACROSSEDGE;
          pos[0] = pu[1];  // [B, C]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (+00)
          // [P, Q] passes C.
          types[0] = (int) ACROSSVERT;
          pos[0] = pu[2];  // C
          pos[1] = 0;  // [P, Q]
        }
      }
    } else { // s1 == 0
      if (s2 > 0) {
        if (s3 > 0) { // (0++)
          // [P, Q] intersects [A, B].
          types[0] = (int) ACROSSEDGE;
          pos[0] = pu[0];  // [A, B]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (0+0)
          // [P, Q] passes A.
          types[0] = (int) ACROSSVERT;
          pos[0] = pu[0];  // A
          pos[1] = 0;  // [P, Q]
        }
      } else { // s2 == 0
        if (s3 > 0) { // (00+)
          // [P, Q] passes B.
          types[0] = (int) ACROSSVERT;
          pos[0] = pu[1];  // B
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (000)
          // Impossible.
          assert(0);
        }
      }
    }
  } else { // z1 == 1
    if (s1 > 0) {
      if (s2 > 0) {
        if (s3 > 0) { // (+++)
          // Q lies in [A, B, C].
          types[0] = (int) TOUCHFACE;
          pos[0] = 0; // [A, B, C]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (++0)
          // Q lies on [C, A].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[2]; // [C, A]
          pos[1] = pv[1]; // Q
        }
      } else { // s2 == 0
        if (s3 > 0) { // (+0+)
          // Q lies on [B, C].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[1]; // [B, C]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (+00)
          // Q = C.
          types[0] = (int) SHAREVERT;
          pos[0] = pu[2]; // C
          pos[1] = pv[1]; // Q
        }
      }
    } else { // s1 == 0
      if (s2 > 0) {
        if (s3 > 0) { // (0++)
          // Q lies on [A, B].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[0]; // [A, B]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (0+0)
          // Q = A.
          types[0] = (int) SHAREVERT;
          pos[0] = pu[0]; // A
          pos[1] = pv[1]; // Q
        }
      } else { // s2 == 0
        if (s3 > 0) { // (00+)
          // Q = B.
          types[0] = (int) SHAREVERT;
          pos[0] = pu[1]; // B
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (000)
          // Impossible.
          assert(0);
        }
      }
    }
  }

  // T and E intersect in a single point.
  return 2;
}

int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, 
                              point R, int level, int *types, int *pos)
{
  REAL sP, sQ;

  // Test the locations of P and Q with respect to ABC.
  sP = orient3d(A, B, C, P);
  sQ = orient3d(A, B, C, Q);

  return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_tri_inter()    Test whether two triangle (abc) and (opq) are          //
//                    intersecting or not.                                   //
//                                                                           //
// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of //
// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C,  REAL* P, 
                                    REAL* Q, REAL s_p, REAL s_q)
{
  int types[2], pos[4];
  int ni;  // =0, 2, 4

  ni = tri_edge_tail(A, B, C, P, Q, nullptr, s_p, s_q, 1, types, pos);

  if (ni > 0) {
    if (ni == 2) {
      // Get the intersection type.
      if (types[0] == (int) SHAREVERT) {
        return (int) SHAREVERT;
      } else {
        return (int) INTERSECT;
      }
    } else if (ni == 4) { 
      // There may be two intersections.
      if (types[0] == (int) SHAREVERT) {
        if (types[1] == (int) DISJOINT) {
          return (int) SHAREVERT;
        } else {
          assert(types[1] != (int) SHAREVERT);
          return (int) INTERSECT;
        }
      } else {
        if (types[0] == (int) SHAREEDGE) {
          return (int) SHAREEDGE;
        } else {
          return (int) INTERSECT;
        }
      }
    } else {
      assert(0);
    }
  }

  return (int) DISJOINT;
}

int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q)
{
  REAL s_o, s_p, s_q;
  REAL s_a, s_b, s_c;

  s_o = orient3d(A, B, C, O);
  s_p = orient3d(A, B, C, P);
  s_q = orient3d(A, B, C, Q);
  if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) {
    // o, p, q are all in the same halfspace of ABC.
    return 0; // DISJOINT;
  }

  s_a = orient3d(O, P, Q, A);
  s_b = orient3d(O, P, Q, B);
  s_c = orient3d(O, P, Q, C);
  if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) {
    // a, b, c are all in the same halfspace of OPQ.
    return 0; // DISJOINT;
  }

  int abcop, abcpq, abcqo;
  int shareedge = 0;

  abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p);
  if (abcop == (int) INTERSECT) {
    return (int) INTERSECT;
  } else if (abcop == (int) SHAREEDGE) {
    shareedge++;
  }
  abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
  if (abcpq == (int) INTERSECT) {
    return (int) INTERSECT;
  } else if (abcpq == (int) SHAREEDGE) {
    shareedge++;
  }
  abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
  if (abcqo == (int) INTERSECT) {
    return (int) INTERSECT;
  } else if (abcqo == (int) SHAREEDGE) {
    shareedge++;
  }
  if (shareedge == 3) {
    // opq are coincident with abc.
    return (int) SHAREFACE;
  }

  // It is only possible either no share edge or one.
  assert(shareedge == 0 || shareedge == 1);

  // Continue to detect whether opq and abc are intersecting or not.
  int opqab, opqbc, opqca;

  opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
  if (opqab == (int) INTERSECT) {
    return (int) INTERSECT;
  }
  opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
  if (opqbc == (int) INTERSECT) {
    return (int) INTERSECT;
  }
  opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
  if (opqca == (int) INTERSECT) {
    return (int) INTERSECT;
  }

  // At this point, two triangles are not intersecting and not coincident.
  //   They may be share an edge, or share a vertex, or disjoint.
  if (abcop == (int) SHAREEDGE) {
    assert((abcpq == (int) SHAREVERT) && (abcqo == (int) SHAREVERT));
    // op is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }
  if (abcpq == (int) SHAREEDGE) {
    assert((abcop == (int) SHAREVERT) && (abcqo == (int) SHAREVERT));
    // pq is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }
  if (abcqo == (int) SHAREEDGE) {
    assert((abcop == (int) SHAREVERT) && (abcpq == (int) SHAREVERT));
    // qo is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }

  // They may share a vertex or disjoint.
  if (abcop == (int) SHAREVERT) {
    // o or p is coincident with a vertex of abc.
    if (abcpq == (int) SHAREVERT) {
      // p is the coincident vertex.
      assert(abcqo != (int) SHAREVERT);
    } else {
      // o is the coincident vertex.
      assert(abcqo == (int) SHAREVERT);
    }
    return (int) SHAREVERT;
  }
  if (abcpq == (int) SHAREVERT) {
    // q is the coincident vertex.
    assert(abcqo == (int) SHAREVERT);
    return (int) SHAREVERT;
  }

  // They are disjoint.
  return (int) DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lu_decmp()    Compute the LU decomposition of a matrix.                   //
//                                                                           //
// Compute the LU decomposition of a (non-singular) square matrix A using    //
// partial pivoting and implicit row exchanges.  The result is:              //
//     A = P * L * U,                                                        //
// where P is a permutation matrix, L is unit lower triangular, and U is     //
// upper triangular.  The factored form of A is used in combination with     //
// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix.       //
//                                                                           //
// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.//
// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- //
// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row   //
// permutation effected by the partial pivoting, effectively,  'ps' array    //
// tells the user what the permutation matrix P is; 'd' is output as +1/-1   //
// depending on whether the number of row interchanges was even or odd,      //
// respectively.                                                             //
//                                                                           //
// Return true if the LU decomposition is successfully computed, otherwise,  //
// return false in case that A is a singular matrix.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N)
{
  REAL scales[4];
  REAL pivot, biggest, mult, tempf;
  int pivotindex = 0;
  int i, j, k;

  *d = 1.0;                                      // No row interchanges yet.

  for (i = N; i < n + N; i++) {                             // For each row.
    // Find the largest element in each row for row equilibration
    biggest = 0.0;
    for (j = N; j < n + N; j++)
      if (biggest < (tempf = fabs(lu[i][j])))
        biggest  = tempf;
    if (biggest != 0.0)
      scales[i] = 1.0 / biggest;
    else {
      scales[i] = 0.0;
      return false;                            // Zero row: singular matrix.
    }
    ps[i] = i;                                 // Initialize pivot sequence.
  }

  for (k = N; k < n + N - 1; k++) {                      // For each column.
    // Find the largest element in each column to pivot around.
    biggest = 0.0;
    for (i = k; i < n + N; i++) {
      if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) {
        biggest = tempf;
        pivotindex = i;
      }
    }
    if (biggest == 0.0) {
      return false;                         // Zero column: singular matrix.
    }
    if (pivotindex != k) {                         // Update pivot sequence.
      j = ps[k];
      ps[k] = ps[pivotindex];
      ps[pivotindex] = j;
      *d = -(*d);                          // ...and change the parity of d.
    }

    // Pivot, eliminating an extra variable  each time
    pivot = lu[ps[k]][k];
    for (i = k + 1; i < n + N; i++) {
      lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot;
      if (mult != 0.0) {
        for (j = k + 1; j < n + N; j++)
          lu[ps[i]][j] -= mult * lu[ps[k]][j];
      }
    }
  }

  // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular.
  return lu[ps[n + N - 1]][n + N - 1] != 0.0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lu_solve()    Solves the linear equation:  Ax = b,  after the matrix A    //
//               has been decomposed into the lower and upper triangular     //
//               matrices L and U, where A = LU.                             //
//                                                                           //
// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as    //
// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]'  //
// is input as the permutation vector returned by 'lu_decmp';  'b[N..n+N-1]' //
// is input as the right-hand side vector, and returns with the solution     //
// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be   //
// left in place for successive calls with different right-hand sides 'b'.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N)
{
  int i, j;
  REAL X[4], dot;

  for (i = N; i < n + N; i++) X[i] = 0.0;

  // Vector reduction using U triangular matrix.
  for (i = N; i < n + N; i++) {
    dot = 0.0;
    for (j = N; j < i + N; j++)
      dot += lu[ps[i]][j] * X[j];
    X[i] = b[ps[i]] - dot;
  }

  // Back substitution, in L triangular matrix.
  for (i = n + N - 1; i >= N; i--) {
    dot = 0.0;
    for (j = i + 1; j < n + N; j++)
      dot += lu[ps[i]][j] * X[j];
    X[i] = (X[i] - dot) / lu[ps[i]][i];
  }

  for (i = N; i < n + N; i++) b[i] = X[i];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incircle3d()    3D in-circle test.                                        //
//                                                                           //
// Return a negative value if pd is inside the circumcircle of the triangle  //
// pa, pb, and pc.                                                           //
//                                                                           //
// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input  //
// triangles are [a,b,c] and [b,a,d].                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd)
{
  REAL area2[2], n1[3], n2[3], c[3];
  REAL sign, r, d;

  // Calculate the areas of the two triangles [a, b, c] and [b, a, d].
  facenormal(pa, pb, pc, n1, 1, nullptr);
  area2[0] = dot(n1, n1);
  facenormal(pb, pa, pd, n2, 1, nullptr);
  area2[1] = dot(n2, n2);

  if (area2[0] > area2[1]) {
    // Choose [a, b, c] as the base triangle.
    circumsphere(pa, pb, pc, nullptr, c, &r);
    d = distance(c, pd);
  } else {
    // Choose [b, a, d] as the base triangle.
    if (area2[1] > 0) {
      circumsphere(pb, pa, pd, nullptr, c, &r);
      d = distance(c, pc);
    } else {
      // The four points are collinear. This case only happens on the boundary.
      return 0; // Return "not inside".
    }
  }

  sign = d - r;
  if (fabs(sign) / r < b->epsilon) {
    sign = 0;
  }

  return sign;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facenormal()    Calculate the normal of the face.                         //
//                                                                           //
// The normal of the face abc can be calculated by the cross product of 2 of //
// its 3 edge vectors.  A better choice of two edge vectors will reduce the  //
// numerical error during the calculation.  Burdakov proved that the optimal //
// basis problem is equivalent to the minimum spanning tree problem with the //
// edge length be the functional, see Burdakov, "A greedy algorithm for the  //
// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two //
// short edges in abc are chosen for the calculation.                        //
//                                                                           //
// If 'lav' is not nullptr and if 'pivot' is set, the average edge length of    //
// the edges of the face [a,b,c] is returned.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot,
                            REAL* lav)
{
  REAL v1[3], v2[3], v3[3], *pv1, *pv2;
  REAL L1, L2, L3;

  v1[0] = pb[0] - pa[0];  // edge vector v1: a->b
  v1[1] = pb[1] - pa[1];
  v1[2] = pb[2] - pa[2];
  v2[0] = pa[0] - pc[0];  // edge vector v2: c->a
  v2[1] = pa[1] - pc[1];
  v2[2] = pa[2] - pc[2];

  // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal).
  if (pivot > 0) {
    // Choose edge vectors by Burdakov's algorithm.
    v3[0] = pc[0] - pb[0];  // edge vector v3: b->c
    v3[1] = pc[1] - pb[1];
    v3[2] = pc[2] - pb[2];
    L1 = dot(v1, v1);
    L2 = dot(v2, v2);
    L3 = dot(v3, v3);
    // Sort the three edge lengths.
    if (L1 < L2) {
      if (L2 < L3) {
        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
      } else {
        pv1 = v3; pv2 = v1; // n = v3 x (-v1).
      }
    } else {
      if (L1 < L3) {
        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
      } else {
        pv1 = v2; pv2 = v3; // n = v2 x (-v3).
      }
    }
    if (lav) {
      // return the average edge length.
      *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0;
    }
  } else {
    pv1 = v1; pv2 = v2; // n = v1 x (-v2).
  }

  // Calculate the face normal.
  cross(pv1, pv2, n);
  // Inverse the direction;
  n[0] = -n[0];
  n[1] = -n[1];
  n[2] = -n[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shortdistance()    Returns the shortest distance from point p to a line   //
//                    defined by two points e1 and e2.                       //
//                                                                           //
// First compute the projection length l_p of the vector v1 = p - e1 along   //
// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the  //
// shortest distance.                                                        //
//                                                                           //
// This routine allows that p is collinear with the line. In this case, the  //
// return value is zero. The two points e1 and e2 should not be identical.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2)
{
  REAL v1[3], v2[3];
  REAL len, l_p;

  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  v2[0] = p[0] - e1[0];
  v2[1] = p[1] - e1[1];
  v2[2] = p[2] - e1[2];

  len = sqrt(dot(v1, v1));
  assert(len != 0.0);

  v1[0] /= len;
  v1[1] /= len;
  v1[2] /= len;
  l_p = dot(v1, v2);

  return sqrt(dot(v2, v2) - l_p * l_p);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// triarea()    Return the area of a triangle.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc)
{
  REAL A[4][4];  

  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)

  cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)

  return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].
}

REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
  REAL adx, bdx, cdx;
  REAL ady, bdy, cdy;
  REAL adz, bdz, cdz;

  adx = pa[0] - pd[0];
  bdx = pb[0] - pd[0];
  cdx = pc[0] - pd[0];
  ady = pa[1] - pd[1];
  bdy = pb[1] - pd[1];
  cdy = pc[1] - pd[1];
  adz = pa[2] - pd[2];
  bdz = pb[2] - pd[2];
  cdz = pc[2] - pd[2];

  return adx * (bdy * cdz - bdz * cdy)
       + bdx * (cdy * adz - cdz * ady)
       + cdx * (ady * bdz - adz * bdy);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interiorangle()    Return the interior angle (0 - 2 * PI) between vectors //
//                    o->p1 and o->p2.                                       //
//                                                                           //
// 'n' is the normal of the plane containing face (o, p1, p2).  The interior //
// angle is the total angle rotating from o->p1 around n to o->p2.  Exchange //
// the position of p1 and p2 will get the complement angle of the other one. //
// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1).  Set  //
// 'n' be nullptr if you only want the interior angle between 0 - PI.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n)
{
  REAL v1[3], v2[3], np[3];
  REAL theta, costheta, lenlen;
  REAL ori, len1, len2;

  // Get the interior angle (0 - PI) between o->p1, and o->p2.
  v1[0] = p1[0] - o[0];
  v1[1] = p1[1] - o[1];
  v1[2] = p1[2] - o[2];
  v2[0] = p2[0] - o[0];
  v2[1] = p2[1] - o[1];
  v2[2] = p2[2] - o[2];
  len1 = sqrt(dot(v1, v1));
  len2 = sqrt(dot(v2, v2));
  lenlen = len1 * len2;
  assert(lenlen != 0.0);

  costheta = dot(v1, v2) / lenlen;
  if (costheta > 1.0) {
    costheta = 1.0; // Roundoff. 
  } else if (costheta < -1.0) {
    costheta = -1.0; // Roundoff. 
  }
  theta = acos(costheta);
  if (n != nullptr) {
    // Get a point above the face (o, p1, p2);
    np[0] = o[0] + n[0];
    np[1] = o[1] + n[1];
    np[2] = o[2] + n[2];
    // Adjust theta (0 - 2 * PI).
    ori = orient3d(p1, o, np, p2);
    if (ori > 0.0) {
      theta = 2 * PI - theta;
    }
  }

  return theta;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// projpt2edge()    Return the projection point from a point to an edge.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj)
{
  REAL v1[3], v2[3];
  REAL len, l_p;

  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  v2[0] = p[0] - e1[0];
  v2[1] = p[1] - e1[1];
  v2[2] = p[2] - e1[2];

  len = sqrt(dot(v1, v1));
  assert(len != 0.0);
  v1[0] /= len;
  v1[1] /= len;
  v1[2] /= len;
  l_p = dot(v1, v2);

  prj[0] = e1[0] + l_p * v1[0];
  prj[1] = e1[1] + l_p * v1[1];
  prj[2] = e1[2] + l_p * v1[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// projpt2face()    Return the projection point from a point to a face.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj)
{
  REAL fnormal[3], v1[3];
  REAL len, dist;

  // Get the unit face normal.
  facenormal(f1, f2, f3, fnormal, 1, nullptr);
  len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + 
             fnormal[2]*fnormal[2]);
  fnormal[0] /= len;
  fnormal[1] /= len;
  fnormal[2] /= len;
  // Get the vector v1 = |p - f1|.
  v1[0] = p[0] - f1[0];
  v1[1] = p[1] - f1[1];
  v1[2] = p[2] - f1[2];
  // Get the project distance.
  dist = dot(fnormal, v1);
  
  // Get the project point.
  prj[0] = p[0] - dist * fnormal[0];
  prj[1] = p[1] - dist * fnormal[1];
  prj[2] = p[2] - dist * fnormal[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facedihedral()    Return the dihedral angle (in radian) between two       //
//                   adjoining faces.                                        //
//                                                                           //
// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are   //
// apexes of these two faces.  Return the angle (between 0 to 2*pi) between  //
// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2).        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2)
{
  REAL n1[3], n2[3];
  REAL n1len, n2len;
  REAL costheta, ori;
  REAL theta;

  facenormal(pa, pb, pc1, n1, 1, nullptr);
  facenormal(pa, pb, pc2, n2, 1, nullptr);
  n1len = sqrt(dot(n1, n1));
  n2len = sqrt(dot(n2, n2));
  costheta = dot(n1, n2) / (n1len * n2len);
  // Be careful rounding error!
  if (costheta > 1.0) {
    costheta = 1.0;
  } else if (costheta < -1.0) {
    costheta = -1.0;
  }
  theta = acos(costheta);
  ori = orient3d(pa, pb, pc1, pc2);
  if (ori > 0.0) {
    theta = 2 * PI - theta;
  }

  return theta;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetalldihedral()    Get all (six) dihedral angles of a tet.               //
//                                                                           //
// If 'cosdd' is not nullptr, it returns the cosines of the 6 dihedral angles,  //
// the edge indices are given in the global array 'edge2ver'. If 'cosmaxd'   //
// (or 'cosmind') is not nullptr, it returns the cosine of the maximal (or      //
// minimal) dihedral angle.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd,
                                REAL* cosdd, REAL* cosmaxd, REAL* cosmind)
{
  REAL N[4][3], vol, cosd, len;
  int f1 = 0, f2 = 0, i, j;

  vol = 0; // Check if the tet is valid or not.

  // Get four normals of faces of the tet.
  tetallnormal(pa, pb, pc, pd, N, &vol);

  if (vol > 0) {
    // Normalize the normals.
    for (i = 0; i < 4; i++) {
      len = sqrt(dot(N[i], N[i]));
      if (len != 0.0) {
        for (j = 0; j < 3; j++) N[i][j] /= len;
      } else {
        // There are degeneracies, such as duplicated vertices.
        vol = 0; //assert(0);
      }
    }
  }

  if (vol <= 0) { // if (vol == 0.0) {
    // A degenerated tet or an inverted tet.
    facenormal(pc, pb, pd, N[0], 1, nullptr);
    facenormal(pa, pc, pd, N[1], 1, nullptr);
    facenormal(pb, pa, pd, N[2], 1, nullptr);
    facenormal(pa, pb, pc, N[3], 1, nullptr);
    // Normalize the normals.
    for (i = 0; i < 4; i++) {
      len = sqrt(dot(N[i], N[i]));
      if (len != 0.0) {
        for (j = 0; j < 3; j++) N[i][j] /= len;
      } else {
        // There are degeneracies, such as duplicated vertices.
        break; // Not a valid normal.
      }
    }
    if (i < 4) {
      // Do not calculate dihedral angles.
      // Set all angles be 0 degree. There will be no quality optimization for
      //   this tet! Use volume optimization to correct it.
      if (cosdd != nullptr) {
        for (i = 0; i < 6; i++) {
          cosdd[i] = -1.0; // 180 degree.
        }
      }
      // This tet has zero volume.
      if (cosmaxd != nullptr) {
        *cosmaxd = -1.0; // 180 degree.
      }
      if (cosmind != nullptr) {
        *cosmind = -1.0; // 180 degree.
      }
      return false;
    }
  }

  // Calculate the consine of the dihedral angles of the edges.
  for (i = 0; i < 6; i++) {
    switch (i) {
    case 0: f1 = 0; f2 = 1; break; // [c,d].
    case 1: f1 = 1; f2 = 2; break; // [a,d].
    case 2: f1 = 2; f2 = 3; break; // [a,b].
    case 3: f1 = 0; f2 = 3; break; // [b,c].
    case 4: f1 = 2; f2 = 0; break; // [b,d].
    case 5: f1 = 1; f2 = 3; break; // [a,c].
    }
    cosd = -dot(N[f1], N[f2]);
    if (cosd < -1.0) cosd = -1.0; // Rounding.
    if (cosd >  1.0) cosd =  1.0; // Rounding.
    if (cosdd) cosdd[i] = cosd;
    if (cosmaxd || cosmind) {
      if (i == 0) {
        if (cosmaxd) *cosmaxd = cosd;
        if (cosmind) *cosmind = cosd;
      } else {
        if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd;
        if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind;
      }
    }
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetallnormal()    Get the in-noramls of the four faces of a given tet.    //
//                                                                           //
// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd,   //
// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices   //
// of the mesh data structure). These normals are unnormalized.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd,
                              REAL N[4][3], REAL* volume)
{
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i, j;

  // get the entries of A[3][3].
  for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i];  // d->a vec
  for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i];  // d->b vec
  for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i];  // d->c vec

  // Compute the inverse of matrix A, to get 3 normals of the 4 faces.
  if (lu_decmp(A, 3, indx, &D, 0)) { // Decompose the matrix just once.
    if (volume != nullptr) {
      // Get the volume of the tet.
      *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0;
    }
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) rhs[i] = 0.0;
      rhs[j] = 1.0;  // Positive means the inside direction
      lu_solve(A, 3, indx, rhs, 0);
      for (i = 0; i < 3; i++) N[j][i] = rhs[i];
    }
    // Get the fourth normal by summing up the first three.
    for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
  } else {
    // The tet is degenerated.
    if (volume != nullptr) {
      *volume = 0;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetaspectratio()    Calculate the aspect ratio of the tetrahedron.        //
//                                                                           //
// The aspect ratio of a tet is R/h, where R is the circumradius and h is    //
// the shortest height of the tet.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd)
{
  REAL vda[3], vdb[3], vdc[3];
  REAL N[4][3], A[4][4], rhs[4], D;
  REAL H[4], volume, radius2, minheightinv;
  int indx[4];
  int i, j; 

  // Set the matrix A = [vda, vdb, vdc]^T.
  for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
  for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
  for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];
  // Lu-decompose the matrix A.
  lu_decmp(A, 3, indx, &D, 0);
  // Get the volume of abcd.
  volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
  // Check if it is zero.
  if (volume == 0.0) return 1.0e+200; // A degenerate tet.
  // if (volume < 0.0) volume = -volume;
  // Check the radiu-edge ratio of the tet.
  rhs[0] = 0.5 * dot(vda, vda);
  rhs[1] = 0.5 * dot(vdb, vdb);
  rhs[2] = 0.5 * dot(vdc, vdc);
  lu_solve(A, 3, indx, rhs, 0);
  // Get the circumcenter.
  // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i];
  // Get the square of the circumradius.
  radius2 = dot(rhs, rhs);

  // Compute the 4 face normals (N[0], ..., N[3]).
  for (j = 0; j < 3; j++) {
    for (i = 0; i < 3; i++) rhs[i] = 0.0;
    rhs[j] = 1.0;  // Positive means the inside direction
    lu_solve(A, 3, indx, rhs, 0);
    for (i = 0; i < 3; i++) N[j][i] = rhs[i];
  }
  // Get the fourth normal by summing up the first three.
  for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
  // Normalized the normals.
  for (i = 0; i < 4; i++) {
    // H[i] is the inverse of the height of its corresponding face.
    H[i] = sqrt(dot(N[i], N[i]));
    // if (H[i] > 0.0) {
    //   for (j = 0; j < 3; j++) N[i][j] /= H[i];
    // }
  }
  // Get the radius of the inscribed sphere.
  // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
  // Get the biggest H[i] (corresponding to the smallest height).
  minheightinv = H[0];
  for (i = 1; i < 3; i++) {
    if (H[i] > minheightinv) minheightinv = H[i];
  }

  return sqrt(radius2) * minheightinv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// circumsphere()    Calculate the smallest circumsphere (center and radius) //
//                   of the given three or four points.                      //
//                                                                           //
// The circumsphere of four points (a tetrahedron) is unique if they are not //
// degenerate. If 'pd = nullptr', the smallest circumsphere of three points is  //
// the diametral sphere of the triangle if they are not degenerate.          //
//                                                                           //
// Return TRUE if the input points are not degenerate and the circumcenter   //
// and circumradius are returned in 'cent' and 'radius' respectively if they //
// are not nullptrs. Otherwise, return FALSE indicated the points are degenrate.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, 
                              REAL* cent, REAL* radius)
{
  REAL A[4][4], rhs[4], D;
  int indx[4];

  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2];
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2];
  if (pd != nullptr) {
    A[2][0] = pd[0] - pa[0];
    A[2][1] = pd[1] - pa[1]; 
    A[2][2] = pd[2] - pa[2];
  } else {
    cross(A[0], A[1], A[2]);
  }

  // Compute the right hand side vector b (3x1).
  rhs[0] = 0.5 * dot(A[0], A[0]);
  rhs[1] = 0.5 * dot(A[1], A[1]);
  if (pd != nullptr) {
    rhs[2] = 0.5 * dot(A[2], A[2]);
  } else {
    rhs[2] = 0.0;
  }

  // Solve the 3 by 3 equations use LU decomposition with partial pivoting
  //   and backward and forward substitute..
  if (!lu_decmp(A, 3, indx, &D, 0)) {
    if (radius != (REAL *) nullptr) *radius = 0.0;
    return false;
  }    
  lu_solve(A, 3, indx, rhs, 0);
  if (cent != (REAL *) nullptr) {
    cent[0] = pa[0] + rhs[0];
    cent[1] = pa[1] + rhs[1];
    cent[2] = pa[2] + rhs[2];
  }
  if (radius != (REAL *) nullptr) {
    *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// orthosphere()    Calulcate the orthosphere of four weighted points.       //
//                                                                           //
// A weighted point (p, P^2) can be interpreted as a sphere centered at the  //
// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 +    //
// p[1]^2 + p[2]^2 - P^2.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd,
                             REAL  aheight, REAL bheight, REAL cheight, 
                             REAL  dheight, REAL* orthocent, REAL* radius)
{
  REAL A[4][4], rhs[4], D;
  int indx[4];

  // Set the coefficient matrix A (4 x 4).
  A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2];
  A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2];
  A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2];
  A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2];

  // Set the right hand side vector (4 x 1).
  rhs[0] = 0.5 * aheight;
  rhs[1] = 0.5 * bheight;
  rhs[2] = 0.5 * cheight;
  rhs[3] = 0.5 * dheight;

  // Solve the 4 by 4 equations use LU decomposition with partial pivoting
  //   and backward and forward substitute..
  if (!lu_decmp(A, 4, indx, &D, 0)) {
    if (radius != (REAL *) nullptr) *radius = 0.0;
    return false;
  }
  lu_solve(A, 4, indx, rhs, 0);

  if (orthocent != (REAL *) nullptr) {
    orthocent[0] = rhs[1];
    orthocent[1] = rhs[2];
    orthocent[2] = rhs[3];
  }
  if (radius != (REAL *) nullptr) {
    // rhs[0] = - rheight / 2;
    // rheight  = - 2 * rhs[0];
    //          =  r[0]^2 + r[1]^2 + r[2]^2 - radius^2
    // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight
    //          = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0]
    *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3]
                   + 2.0 * rhs[0]);
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// planelineint()    Calculate the intersection of a line and a plane.       //
//                                                                           //
// The equation of a plane (points P are on the plane with normal N and P3   //
// on the plane) can be written as: N dot (P - P3) = 0. The equation of the  //
// line (points P on the line passing through P1 and P2) can be written as:  //
// P = P1 + u (P2 - P1). The intersection of these two occurs when:          //
//   N dot (P1 + u (P2 - P1)) = N dot P3.                                    //
// Solving for u gives:                                                      //
//         N dot (P3 - P1)                                                   //
//   u = ------------------.                                                 //
//         N dot (P2 - P1)                                                   //
// If the denominator is 0 then N (the normal to the plane) is perpendicular //
// to the line.  Thus the line is either parallel to the plane and there are //
// no solutions or the line is on the plane in which case there are an infi- //
// nite number of solutions.                                                 //
//                                                                           //
// The plane is given by three points pa, pb, and pc, e1 and e2 defines the  //
// line. If u is non-zero, The intersection point (if exists) returns in ip. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2,
                              REAL* ip, REAL* u)
{
  REAL n[3], det, det1;

  // Calculate N.
  facenormal(pa, pb, pc, n, 1, nullptr);
  // Calculate N dot (e2 - e1).
  det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1])
      + n[2] * (e2[2] - e1[2]);
  if (det != 0.0) {
    // Calculate N dot (pa - e1)
    det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1])
         + n[2] * (pa[2] - e1[2]);
    *u = det1 / det;
    ip[0] = e1[0] + *u * (e2[0] - e1[0]);
    ip[1] = e1[1] + *u * (e2[1] - e1[1]);
    ip[2] = e1[2] + *u * (e2[2] - e1[2]);
  } else {
    *u = 0.0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// linelineint()    Calculate the intersection(s) of two line segments.      //
//                                                                           //
// Calculate the line segment [P, Q] that is the shortest route betweeb two  //
// lines from A to B and C to D. Calculate also the values of tp and tq      //
// where: P = A + tp (B - A), and Q = C + tq (D - C).                        //
//                                                                           //
// Return 1 if the line segment exists. Otherwise, return 0.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, 
                            REAL* Q, REAL* tp, REAL* tq)
{
  REAL vab[3], vcd[3], vca[3];
  REAL vab_vab, vcd_vcd, vab_vcd;
  REAL vca_vab, vca_vcd;
  REAL det, eps;
  int i;

  for (i = 0; i < 3; i++) {
    vab[i] = B[i] - A[i];
    vcd[i] = D[i] - C[i];
    vca[i] = A[i] - C[i];
  }

  vab_vab = dot(vab, vab);
  vcd_vcd = dot(vcd, vcd);
  vab_vcd = dot(vab, vcd);

  det = vab_vab * vcd_vcd - vab_vcd * vab_vcd;
  // Round the result.
  eps = det / (fabs(vab_vab * vcd_vcd) + fabs(vab_vcd * vab_vcd));
  if (eps < b->epsilon) {
    return 0;
  }

  vca_vab = dot(vca, vab);
  vca_vcd = dot(vca, vcd);

  *tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det;
  *tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det;

  for (i = 0; i < 3; i++) P[i] = A[i] + (*tp) * vab[i];
  for (i = 0; i < 3; i++) Q[i] = C[i] + (*tq) * vcd[i];

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetprismvol()    Calculate the volume of a tetrahedral prism in 4D.       //
//                                                                           //
// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-//
// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular  //
// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 //
// vertices. (Wikipedia).                                                    //
//                                                                           //
// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form //
// the lower tetrahedral facet of the prism.  The top tetrahedral facet is   //
// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by     //
// lifting each vertex of the lower facet into R^4 by a weight (height).  A  //
// canonical choice of the weights is the square of Euclidean norm of of the //
// points (vectors).                                                         //
//                                                                           //
//                                                                           //
// The return value is (4!) 24 times of the volume of the tetrahedral prism. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3)
{
  REAL *p4, *p5, *p6, *p7;
  REAL w4, w5, w6, w7;
  REAL vol[4];

  p4 = p0;
  p5 = p1;
  p6 = p2;
  p7 = p3;

  // TO DO: these weights can be pre-calculated!
  w4 = dot(p0, p0);
  w5 = dot(p1, p1);
  w6 = dot(p2, p2);
  w7 = dot(p3, p3);

  // Calculate the volume of the tet-prism.
  vol[0] = orient4d_numeric(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7);
  vol[1] = orient4d_numeric(p3, p6, p2, p0, p1,  0, w6,  0, 0,  0);
  vol[2] = orient4d_numeric(p4, p6, p3, p0, p1, w4, w6,  0, 0,  0);
  vol[3] = orient4d_numeric(p6, p5, p4, p3, p1, w6, w5, w4, 0,  0);

  return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// calculateabovepoint()    Calculate a point above a facet in 'dummypoint'. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa,
                                     point *ppb, point *ppc)
{
  point *ppt, pa, pb, pc;
  REAL v1[3], v2[3], n[3];
  REAL lab, len, A, area;
  REAL x, y, z;
  int i;

  ppt = (point *) fastlookup(facpoints, 0);
  pa = *ppt; // a is the first point.
  pb = pc = nullptr; // Avoid compiler warnings.

  // Get a point b s.t. the length of [a, b] is maximal.
  lab = 0;
  for (i = 1; i < facpoints->objects; i++) {
    ppt = (point *) fastlookup(facpoints, i);
    x = (*ppt)[0] - pa[0];
    y = (*ppt)[1] - pa[1];
    z = (*ppt)[2] - pa[2];
    len = x * x + y * y + z * z;
    if (len > lab) {
      lab = len;
      pb = *ppt;
    }
  }
  lab = sqrt(lab);
  if (lab == 0) {
    if (!b->quiet) {
      printf("Warning:  All points of a facet are coincident with %d.\n",
        pointmark(pa));
    }
    return false;
  }

  // Get a point c s.t. the area of [a, b, c] is maximal.
  v1[0] = pb[0] - pa[0];
  v1[1] = pb[1] - pa[1];
  v1[2] = pb[2] - pa[2];
  A = 0;
  for (i = 1; i < facpoints->objects; i++) {
    ppt = (point *) fastlookup(facpoints, i);
    v2[0] = (*ppt)[0] - pa[0];
    v2[1] = (*ppt)[1] - pa[1];
    v2[2] = (*ppt)[2] - pa[2];
    cross(v1, v2, n);
    area = dot(n, n);
    if (area > A) {
      A = area;
      pc = *ppt;
    }
  }
  if (A == 0) {
    // All points are collinear. No above point.
    if (!b->quiet) {
      printf("Warning:  All points of a facet are collinaer with [%d, %d].\n",
        pointmark(pa), pointmark(pb));
    }
    return false;
  }

  // Calculate an above point of this facet.
  facenormal(pa, pb, pc, n, 1, nullptr);
  len = sqrt(dot(n, n));
  n[0] /= len;
  n[1] /= len;
  n[2] /= len;
  lab /= 2.0; // Half the maximal length.
  dummypoint[0] = pa[0] + lab * n[0];
  dummypoint[1] = pa[1] + lab * n[1];
  dummypoint[2] = pa[2] + lab * n[2];

  if (ppa != nullptr) {
    // Return the three points.
    *ppa = pa;
    *ppb = pb;
    *ppc = pc;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Calculate an above point. It lies above the plane containing  the subface //
//   [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint //
//   is the normal of the plane.                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd)
{
  REAL n1[3], n2[3], *norm;
  REAL len, len1, len2;

  // Select a base.
  facenormal(pa, pb, pc, n1, 1, nullptr);
  len1 = sqrt(dot(n1, n1));
  facenormal(pa, pb, pd, n2, 1, nullptr);
  len2 = sqrt(dot(n2, n2));
  if (len1 > len2) {
    norm = n1;
    len = len1;
  } else {
    norm = n2;
    len = len2;
  }
  assert(len > 0);
  norm[0] /= len;
  norm[1] /= len;
  norm[2] /= len;
  len = distance(pa, pb);
  dummypoint[0] = pa[0] + len * norm[0];
  dummypoint[1] = pa[1] + len * norm[1];
  dummypoint[2] = pa[2] + len * norm[2];
}

////                                                                       ////
////                                                                       ////
//// geom_cxx /////////////////////////////////////////////////////////////////

//// flip_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip23()    Perform a 2-to-3 flip (face-to-edge flip).                    //
//                                                                           //
// 'fliptets' is an array of three tets (handles), where the [0] and [1] are  //
// [a,b,c,d] and [b,a,c,e].  The three new tets: [e,d,a,b], [e,d,b,c], and   //
// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'.  As a result,  //
// The face [a,b,c] is removed, and the edge [d,e] is created.               //
//                                                                           //
// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of   //
// the five vertices may be 'dummypoint'. There are two canonical cases:     //
//   (1) d is 'dummypoint', then all three new tets are hull tets.  If e is  //
//       'dummypoint', we reconfigure e to d, i.e., turn it up-side down.    //
//   (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are  //
//       hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., //
//       rotate the three input tets counterclockwisely (right-hand rule)    //
//       until a or b is in c's position.                                    //
//                                                                           //
// If 'fc->enqflag' is set, convex hull faces will be queued for flipping.   //
// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip()   //
// after the insertion of a new point.  It is assumed that 'd' is the new    //
// point. IN this case, only link faces of 'd' are queued.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc)
{
  triface topcastets[3], botcastets[3];
  triface newface, casface;
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  int dummyflag = 0;  // range = {-1, 0, 1, 2}.
  int i;

  if (hullflag > 0) {
    // Check if e is dummypoint.
    if (oppo(fliptets[1]) == dummypoint) {
      // Swap the two old tets.
      newface = fliptets[0];
      fliptets[0] = fliptets[1];
      fliptets[1] = newface;
      dummyflag = -1;  // d is dummypoint.
    } else {
      // Check if either a or b is dummypoint.
      if (org(fliptets[0]) == dummypoint) {
        dummyflag = 1; // a is dummypoint.
        enextself(fliptets[0]);
        eprevself(fliptets[1]);
      } else if (dest(fliptets[0]) == dummypoint) {
        dummyflag = 2; // b is dummypoint.
        eprevself(fliptets[0]);
        enextself(fliptets[1]);
      } else {
        dummyflag = 0; // either c or d may be dummypoint.
      }
    }
  }

  pa =  org(fliptets[0]);
  pb = dest(fliptets[0]);
  pc = apex(fliptets[0]);
  pd = oppo(fliptets[0]);
  pe = oppo(fliptets[1]);

  flip23count++;

  // Get the outer boundary faces.
  for (i = 0; i < 3; i++) {
    fnext(fliptets[0], topcastets[i]);
    enextself(fliptets[0]);
  }
  for (i = 0; i < 3; i++) {
    fnext(fliptets[1], botcastets[i]);
    eprevself(fliptets[1]);
  }

  // Re-use fliptets[0] and fliptets[1].
  fliptets[0].ver = 11;
  fliptets[1].ver = 11;
  setelemmarker(fliptets[0].tet, 0); // Clear all flags.
  setelemmarker(fliptets[1].tet, 0);
  // NOTE: the element attributes and volume constraint remain unchanged.
  if (checksubsegflag) {
    // Dealloc the space to subsegments.
    if (fliptets[0].tet[8] != nullptr) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = nullptr;
    }
    if (fliptets[1].tet[8] != nullptr) {
      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
      fliptets[1].tet[8] = nullptr;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != nullptr) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = nullptr;
    }
    if (fliptets[1].tet[9] != nullptr) {
      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
      fliptets[1].tet[9] = nullptr;
    }
  }
  // Create a new tet.
  maketetrahedron(&(fliptets[2]));
  // The new tet have the same attributes from the old tet.
  for (i = 0; i < numelemattrib; i++) {
    attrib = elemattribute(fliptets[0].tet, i);
    setelemattribute(fliptets[2].tet, i, attrib);
  }
  if (b->varvolume) {
    volume = volumebound(fliptets[0].tet);
    setvolumebound(fliptets[2].tet, volume);
  }

  if (hullflag > 0) {
    // Check if d is dummytet.
    if (pd != dummypoint) {
      setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
      setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
      // Check if c is dummypoint.
      if (pc != dummypoint) {
        setvertices(fliptets[2], pe, pd, pc, pa);  // [e,d,c,a] *
      } else {
        setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c]
        esymself(fliptets[2]);                    // [e,d,c,a] *
      }
      // The hullsize does not change.
    } else {
      // d is dummypoint.
      setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d]
      setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d]
      setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d]
      // Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] *
      for (i = 0; i < 3; i++) {
        eprevesymself(fliptets[i]);
        enextself(fliptets[i]);
      }
      // We deleted one hull tet, and created three hull tets.
      hullsize += 2;
    }
  } else {
    setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
    setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
    setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
  }

  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
    REAL volneg[2], volpos[3], vol_diff;
    if (pd != dummypoint) {
      if (pc != dummypoint) {
        volpos[0] = tetprismvol(pe, pd, pa, pb);
        volpos[1] = tetprismvol(pe, pd, pb, pc);
        volpos[2] = tetprismvol(pe, pd, pc, pa);
        volneg[0] = tetprismvol(pa, pb, pc, pd);
        volneg[1] = tetprismvol(pb, pa, pc, pe);
      } else { // pc == dummypoint
        volpos[0] = tetprismvol(pe, pd, pa, pb);
        volpos[1] = 0.;
        volpos[2] = 0.;
        volneg[0] = 0.;
        volneg[1] = 0.;
      }
    } else { // pd == dummypoint.
      volpos[0] = 0.;
      volpos[1] = 0.;
      volpos[2] = 0.;
      volneg[0] = 0.;
      volneg[1] = tetprismvol(pb, pa, pc, pe);
    }
    vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1];
    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
  }

  // Bond three new tets together.
  for (i = 0; i < 3; i++) {
    esym(fliptets[i], newface);
    bond(newface, fliptets[(i + 1) % 3]);
  }
  // Bond to top outer boundary faces (at [a,b,c,d]).
  for (i = 0; i < 3; i++) {
    eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c].
    bond(newface, topcastets[i]);
  }
  // Bond bottom outer boundary faces (at [b,a,c,e]).
  for (i = 0; i < 3; i++) {
    edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a].
    bond(newface, botcastets[i]);
  }

  if (checksubsegflag) {
    // Bond subsegments if there are.
    // Each new tet has 5 edges to be checked (except the edge [e,d]). 
    face checkseg;
    // The middle three: [a,b], [b,c], [c,a].
    for (i = 0; i < 3; i++) {      
      if (issubseg(topcastets[i])) {
        tsspivot1(topcastets[i], checkseg);
        eorgoppo(fliptets[i], newface);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
    }
    // The top three: [d,a], [d,b], [d,c]. Two tets per edge.
    for (i = 0; i < 3; i++) {
      eprev(topcastets[i], casface);      
      if (issubseg(casface)) {
        tsspivot1(casface, checkseg);
        enext(fliptets[i], newface);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        esym(fliptets[(i + 2) % 3], newface);
        eprevself(newface);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
    }
    // The bot three: [a,e], [b,e], [c,e]. Two tets per edge.
    for (i = 0; i < 3; i++) {
      enext(botcastets[i], casface);
      if (issubseg(casface)) {
        tsspivot1(casface, checkseg);
        eprev(fliptets[i], newface);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        esym(fliptets[(i + 2) % 3], newface);
        enextself(newface);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
    }
  } // if (checksubsegflag)

  if (checksubfaceflag) {
    // Bond 6 subfaces if there are.
    face checksh;
    for (i = 0; i < 3; i++) {      
      if (issubface(topcastets[i])) {
        tspivot(topcastets[i], checksh);
        eorgoppo(fliptets[i], newface);
        sesymself(checksh);
        tsbond(newface, checksh);
        if (fc->chkencflag & 2) {
          enqueuesubface(badsubfacs, &checksh);
        }
      }
    }
    for (i = 0; i < 3; i++) {
      if (issubface(botcastets[i])) {
        tspivot(botcastets[i], checksh);
        edestoppo(fliptets[i], newface);
        sesymself(checksh);
        tsbond(newface, checksh);
        if (fc->chkencflag & 2) {
          enqueuesubface(badsubfacs, &checksh);
        }
      }
    }
  } // if (checksubfaceflag)

  if (fc->chkencflag & 4) {
    // Put three new tets into check list.
    for (i = 0; i < 3; i++) {
      enqueuetetrahedron(&(fliptets[i]));
    }
  }

  // Update the point-to-tet map.
  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pc, (tetrahedron) fliptets[1].tet);
  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pe, (tetrahedron) fliptets[0].tet);

  if (hullflag > 0) {
    if (dummyflag != 0) {
      // Restore the original position of the points (for flipnm()).
      if (dummyflag == -1) { 
        // Reverse the edge.
        for (i = 0; i < 3; i++) {
          esymself(fliptets[i]);
        }
        // Swap the last two new tets.
        newface = fliptets[1];
        fliptets[1] = fliptets[2];
        fliptets[2] = newface;
      } else {
        // either a or b were swapped.
        if (dummyflag == 1) {
          // a is dummypoint.
          newface = fliptets[0];
          fliptets[0] = fliptets[2];
          fliptets[2] = fliptets[1];
          fliptets[1] = newface;
        } else { // dummyflag == 2
          // b is dummypoint.
          newface = fliptets[0];
          fliptets[0] = fliptets[1];
          fliptets[1] = fliptets[2];
          fliptets[2] = newface;
        }
      }
    }
  }

  if (fc->enqflag > 0) {
    // Queue faces which may be locally non-Delaunay.  
    for (i = 0; i < 3; i++) {
      eprevesym(fliptets[i], newface);
      flippush(flipstack, &newface);
    }
    if (fc->enqflag > 1) {
      for (i = 0; i < 3; i++) {
        enextesym(fliptets[i], newface);
        flippush(flipstack, &newface);
      }
    }
  }

  recenttet = fliptets[0];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip32()    Perform a 3-to-2 flip (edge-to-face flip).                    //
//                                                                           //
// 'fliptets' is an array of three tets (handles),  which are [e,d,a,b],     //
// [e,d,b,c], and [e,d,c,a].  The two new tets: [a,b,c,d] and [b,a,c,e] are  //
// returned in [0] and [1] of 'fliptets'.  As a result, the edge [e,d] is    //
// replaced by the face [a,b,c].                                             //
//                                                                           //
// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of   //
// the five vertices may be 'dummypoint'. There are two canonical cases:     //
//   (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',//
//       we reconfigure e to d, i.e., turnover it.                           //
//   (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets.  //
//       If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the //
//       three old tets counterclockwisely (right-hand rule) until a or b    //
//       is in c's position.                                                 //
//                                                                           //
// If 'fc->enqflag' is set, convex hull faces will be queued for flipping.   //
// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip()   //
// after the insertion of a new point.  It is assumed that 'a' is the new    //
// point. In this case, only link faces of 'a' are queued.                   //
//                                                                           //
// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a  //
// segment. There may be two (interior) subfaces sharing at [e,d], which are //
// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c),  //
// or (c,a)  In such case, a 2-to-2 flip is performed on these two subfaces  //
// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted   //
// back into the tetrahedralization.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc)
{
  triface topcastets[3], botcastets[3];
  triface newface, casface;
  face flipshs[3]; 
  face checkseg; 
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  int dummyflag = 0;  // Rangle = {-1, 0, 1, 2}
  int spivot = -1, scount = 0; // for flip22()
  int t1ver; 
  int i, j;

  if (hullflag > 0) {
    // Check if e is 'dummypoint'.
    if (org(fliptets[0]) == dummypoint) {
      // Reverse the edge.
      for (i = 0; i < 3; i++) {
        esymself(fliptets[i]);
      }
      // Swap the last two tets.
      newface = fliptets[1];
      fliptets[1] = fliptets[2];
      fliptets[2] = newface;
      dummyflag = -1; // e is dummypoint.
    } else {
      // Check if a or b is the 'dummypoint'.
      if (apex(fliptets[0]) == dummypoint) { 
        dummyflag = 1;  // a is dummypoint.
        newface = fliptets[0];
        fliptets[0] = fliptets[1];
        fliptets[1] = fliptets[2];
        fliptets[2] = newface;
      } else if (apex(fliptets[1]) == dummypoint) {
        dummyflag = 2;  // b is dummypoint.
        newface = fliptets[0];
        fliptets[0] = fliptets[2];
        fliptets[2] = fliptets[1];
        fliptets[1] = newface;
      } else {
        dummyflag = 0;  // either c or d may be dummypoint.
      }
    }
  }

  pa = apex(fliptets[0]);
  pb = apex(fliptets[1]);
  pc = apex(fliptets[2]);
  pd = dest(fliptets[0]);
  pe = org(fliptets[0]);

  flip32count++;

  // Get the outer boundary faces.
  for (i = 0; i < 3; i++) {
    eorgoppo(fliptets[i], casface);
    fsym(casface, topcastets[i]);
  }
  for (i = 0; i < 3; i++) {
    edestoppo(fliptets[i], casface);
    fsym(casface, botcastets[i]);
  }

  if (checksubfaceflag) {
    // Check if there are interior subfaces at the edge [e,d].
    for (i = 0; i < 3; i++) {
      tspivot(fliptets[i], flipshs[i]);
      if (flipshs[i].sh != nullptr) {
        // Found an interior subface.
        stdissolve(flipshs[i]); // Disconnect the sub-tet bond.
        scount++;
      } else {
        spivot = i;
      }
    }
  }

  // Re-use fliptets[0] and fliptets[1].
  fliptets[0].ver = 11;
  fliptets[1].ver = 11;
  setelemmarker(fliptets[0].tet, 0); // Clear all flags.
  setelemmarker(fliptets[1].tet, 0);
  if (checksubsegflag) {
    // Dealloc the space to subsegments.
    if (fliptets[0].tet[8] != nullptr) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = nullptr;
    }
    if (fliptets[1].tet[8] != nullptr) {
      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
      fliptets[1].tet[8] = nullptr;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != nullptr) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = nullptr;
    }
    if (fliptets[1].tet[9] != nullptr) {
      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
      fliptets[1].tet[9] = nullptr;
    }
  }
  if (checksubfaceflag) {
    if (scount > 0) {
      // The element attributes and volume constraint must be set correctly.
      // There are two subfaces involved in this flip. The three tets are
      //   separated into two different regions, one may be exterior. The
      //   first region has two tets, and the second region has only one.
      //   The two created tets must be in the same region as the first region. 
      //   The element attributes and volume constraint must be set correctly.
      //assert(spivot != -1);
      // The tet fliptets[spivot] is in the first region.
      for (j = 0; j < 2; j++) {
        for (i = 0; i < numelemattrib; i++) {
          attrib = elemattribute(fliptets[spivot].tet, i);
          setelemattribute(fliptets[j].tet, i, attrib);
        }
        if (b->varvolume) {
          volume = volumebound(fliptets[spivot].tet);
          setvolumebound(fliptets[j].tet, volume);
        }
      }
    }
  }
  // Delete an old tet.
  tetrahedrondealloc(fliptets[2].tet);

  if (hullflag > 0) {
    // Check if c is dummypointc.
    if (pc != dummypoint) {
      // Check if d is dummypoint.
      if (pd != dummypoint) {
        // No hull tet is involved.
      } else {
        // We deleted three hull tets, and created one hull tet.
        hullsize -= 2;
      }
      setvertices(fliptets[0], pa, pb, pc, pd);
      setvertices(fliptets[1], pb, pa, pc, pe);
    } else {
      // c is dummypoint. The two new tets are hull tets.
      setvertices(fliptets[0], pb, pa, pd, pc);
      setvertices(fliptets[1], pa, pb, pe, pc);
      // Adjust badc -> abcd.
      esymself(fliptets[0]);
      // Adjust abec -> bace.
      esymself(fliptets[1]);
      // The hullsize does not change.
    }
  } else {
    setvertices(fliptets[0], pa, pb, pc, pd);
    setvertices(fliptets[1], pb, pa, pc, pe);
  }

  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
    REAL volneg[3], volpos[2], vol_diff;
    if (pc != dummypoint) {
      if (pd != dummypoint) {
        volneg[0] = tetprismvol(pe, pd, pa, pb);
        volneg[1] = tetprismvol(pe, pd, pb, pc);
        volneg[2] = tetprismvol(pe, pd, pc, pa);
        volpos[0] = tetprismvol(pa, pb, pc, pd);
        volpos[1] = tetprismvol(pb, pa, pc, pe);
      } else { // pd == dummypoint
        volneg[0] = 0.;
        volneg[1] = 0.;
        volneg[2] = 0.;
        volpos[0] = 0.;
        volpos[1] = tetprismvol(pb, pa, pc, pe);
      }
    } else { // pc == dummypoint.
      volneg[0] = tetprismvol(pe, pd, pa, pb);
      volneg[1] = 0.;
      volneg[2] = 0.;
      volpos[0] = 0.;
      volpos[1] = 0.;
    }
    vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2];
    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
  }

  // Bond abcd <==> bace.
  bond(fliptets[0], fliptets[1]);
  // Bond new faces to top outer boundary faces (at abcd).
  for (i = 0; i < 3; i++) {
    esym(fliptets[0], newface);
    bond(newface, topcastets[i]);
    enextself(fliptets[0]);
  }
  // Bond new faces to bottom outer boundary faces (at bace).
  for (i = 0; i < 3; i++) {
    esym(fliptets[1], newface);
    bond(newface, botcastets[i]);
    eprevself(fliptets[1]);
  }

  if (checksubsegflag) {
    // Bond 9 segments to new (flipped) tets.
    for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a.      
      if (issubseg(topcastets[i])) {
        tsspivot1(topcastets[i], checkseg);
        tssbond1(fliptets[0], checkseg);
        sstbond1(checkseg, fliptets[0]);
        tssbond1(fliptets[1], checkseg);
        sstbond1(checkseg, fliptets[1]);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
      enextself(fliptets[0]);
      eprevself(fliptets[1]);
    }
    // The three top edges.
    for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d.
      esym(fliptets[0], newface);
      eprevself(newface); 
      enext(topcastets[i], casface);      
      if (issubseg(casface)) {
        tsspivot1(casface, checkseg);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
      enextself(fliptets[0]);
    }
    // The three bot edges.
    for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e.
      esym(fliptets[1], newface);
      enextself(newface); 
      eprev(botcastets[i], casface);
      if (issubseg(casface)) {
        tsspivot1(casface, checkseg);
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
      eprevself(fliptets[1]);
    }
  } // if (checksubsegflag)

  if (checksubfaceflag) {
    face checksh;
    // Bond the top three casing subfaces.
    for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c]
      if (issubface(topcastets[i])) {
        tspivot(topcastets[i], checksh);
        esym(fliptets[0], newface); 
        sesymself(checksh);
        tsbond(newface, checksh);
        if (fc->chkencflag & 2) {
          enqueuesubface(badsubfacs, &checksh);
        }
      }
      enextself(fliptets[0]);
    }
    // Bond the bottom three casing subfaces.
    for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a]
      if (issubface(botcastets[i])) {
        tspivot(botcastets[i], checksh);
        esym(fliptets[1], newface); 
        sesymself(checksh);
        tsbond(newface, checksh);
        if (fc->chkencflag & 2) {
          enqueuesubface(badsubfacs, &checksh);
        }
      }
      eprevself(fliptets[1]);
    }

    if (scount > 0) {
      face flipfaces[2];
      // Perform a 2-to-2 flip in subfaces.
      flipfaces[0] = flipshs[(spivot + 1) % 3];
      flipfaces[1] = flipshs[(spivot + 2) % 3];
      sesymself(flipfaces[1]);
      flip22(flipfaces, 0, fc->chkencflag);
      // Connect the flipped subfaces to flipped tets.
      // First go to the corresponding flipping edge.
      //   Re-use top- and botcastets[0].
      topcastets[0] = fliptets[0];
      botcastets[0] = fliptets[1];
      for (i = 0; i < ((spivot + 1) % 3); i++) {
        enextself(topcastets[0]);
        eprevself(botcastets[0]);
      }
      // Connect the top subface to the top tets.
      esymself(topcastets[0]);
      sesymself(flipfaces[0]);
      // Check if there already exists a subface.
      tspivot(topcastets[0], checksh);
      if (checksh.sh == nullptr) {
        tsbond(topcastets[0], flipfaces[0]);
        fsymself(topcastets[0]);
        sesymself(flipfaces[0]);
        tsbond(topcastets[0], flipfaces[0]);
      } else {
        // An invalid 2-to-2 flip.
        assert(0);
      }
      // Connect the bot subface to the bottom tets.
      esymself(botcastets[0]);
      sesymself(flipfaces[1]);
      // Check if there already exists a subface.
      tspivot(botcastets[0], checksh);
      if (checksh.sh == nullptr) {
        tsbond(botcastets[0], flipfaces[1]);
        fsymself(botcastets[0]);
        sesymself(flipfaces[1]);
        tsbond(botcastets[0], flipfaces[1]);
      } else {
        // An invalid 2-to-2 flip.
        assert(0);  
      }
    } // if (scount > 0)
  } // if (checksubfaceflag)

  if (fc->chkencflag & 4) {
    // Put two new tets into check list.
    for (i = 0; i < 2; i++) {
      enqueuetetrahedron(&(fliptets[i]));
    }
  }

  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pe, (tetrahedron) fliptets[1].tet);

  if (hullflag > 0) {
    if (dummyflag != 0) {
      // Restore the original position of the points (for flipnm()).
      if (dummyflag == -1) {
        // e were dummypoint. Swap the two new tets.
        newface = fliptets[0];
        fliptets[0] = fliptets[1];
        fliptets[1] = newface;
      } else {
        // a or b was dummypoint.
        if (dummyflag == 1) {
          eprevself(fliptets[0]);
          enextself(fliptets[1]);
        } else { // dummyflag == 2
          enextself(fliptets[0]);
          eprevself(fliptets[1]);
        }
      }
    }
  }
  
  if (fc->enqflag > 0) {
    // Queue faces which may be locally non-Delaunay.
    // pa = org(fliptets[0]); // 'a' may be a new vertex.
    enextesym(fliptets[0], newface);
    flippush(flipstack, &newface);
    eprevesym(fliptets[1], newface);
    flippush(flipstack, &newface);
    if (fc->enqflag > 1) {
      //pb = dest(fliptets[0]);
      eprevesym(fliptets[0], newface);
      flippush(flipstack, &newface);
      enextesym(fliptets[1], newface);
      flippush(flipstack, &newface);
      //pc = apex(fliptets[0]);
      esym(fliptets[0], newface);
      flippush(flipstack, &newface);
      esym(fliptets[1], newface);
      flippush(flipstack, &newface);
    }
  }

  recenttet = fliptets[0];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip41()    Perform a 4-to-1 flip (Remove a vertex).                      //
//                                                                           //
// 'fliptets' is an array of four tetrahedra in the star of the removing     //
// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The //
// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, //
// p].  On return, 'fliptets[0]' is the new tet [a,b,c,d].                   //
//                                                                           //
// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. //
// The 'hullsize' may be changed.  Note that p may be dummypoint.  In this   //
// case, four hull tets are replaced by one real tet.                        //
//                                                                           //
// If 'checksubface' flag is set (>0), it is possible that there are three   //
// interior subfaces connecting at p.  If so, a 3-to-1 flip is performed to  //
// to remove p from the surface triangulation.                               //
//                                                                           //
// If it is called by the routine incrementalflip(), we assume that d is the //
// newly inserted vertex.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc)
{
  triface topcastets[3], botcastet;
  triface newface, neightet;
  face flipshs[4];
  point pa, pb, pc, pd, pp;
  int dummyflag = 0; // in {0, 1, 2, 3, 4}
  int spivot = -1, scount = 0;
  int t1ver; 
  int i;

  pa =  org(fliptets[3]);
  pb = dest(fliptets[3]);
  pc = apex(fliptets[3]);
  pd = dest(fliptets[0]);
  pp =  org(fliptets[0]); // The removing vertex.

  flip41count++;

  // Get the outer boundary faces.
  for (i = 0; i < 3; i++) {
    enext(fliptets[i], topcastets[i]);
    fnextself(topcastets[i]); // [d,a,b,#], [d,b,c,#], [d,c,a,#]
    enextself(topcastets[i]); // [a,b,d,#], [b,c,d,#], [c,a,d,#]
  }
  fsym(fliptets[3], botcastet); // [b,a,c,#]

  if (checksubfaceflag) {
    // Check if there are three subfaces at 'p'.
    //   Re-use 'newface'.
    for (i = 0; i < 3; i++) {
      fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d].
      tspivot(newface, flipshs[i]);
      if (flipshs[i].sh != nullptr) {
        spivot = i; // Remember this subface.
        scount++;
      }
      enextself(fliptets[3]);
    }
    if (scount > 0) {
      // There are three subfaces connecting at p.
      if (scount < 3) {
        // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}.
        assert(scount == 1); // spivot >= 0
        // Go to the tet containing the three subfaces.
        fsym(topcastets[spivot], neightet);
        // Get the three subfaces connecting at p.
        for (i = 0; i < 3; i++) {
          esym(neightet, newface);
          tspivot(newface, flipshs[i]);
          assert(flipshs[i].sh != nullptr);
          eprevself(neightet);
        }
      } else {
        spivot = 3; // The new subface is [a,b,c].
      }
    }
  } // if (checksubfaceflag)


  // Re-use fliptets[0] for [a,b,c,d].
  fliptets[0].ver = 11;
  setelemmarker(fliptets[0].tet, 0); // Clean all flags.
  // NOTE: the element attributes and volume constraint remain unchanged.
  if (checksubsegflag) {
    // Dealloc the space to subsegments.
    if (fliptets[0].tet[8] != nullptr) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = nullptr;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != nullptr) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = nullptr;
    }
  }
  // Delete the other three tets.
  for (i = 1; i < 4; i++) {
    tetrahedrondealloc(fliptets[i].tet);
  }

  if (pp != dummypoint) {
    // Mark the point pp as unused.
    setpointtype(pp, UNUSEDVERTEX);
    unuverts++;
  }

  // Create the new tet [a,b,c,d].
  if (hullflag > 0) {
    // One of the five vertices may be 'dummypoint'.
    if (pa == dummypoint) {
      // pa is dummypoint.
      setvertices(fliptets[0], pc, pb, pd, pa);
      esymself(fliptets[0]);  // [b,c,a,d]
      eprevself(fliptets[0]); // [a,b,c,d]
      dummyflag = 1;
    } else if (pb == dummypoint) {
      setvertices(fliptets[0], pa, pc, pd, pb);
      esymself(fliptets[0]);  // [c,a,b,d]
      enextself(fliptets[0]); // [a,b,c,d]
      dummyflag = 2;
    } else if (pc == dummypoint) {
      setvertices(fliptets[0], pb, pa, pd, pc);
      esymself(fliptets[0]);  // [a,b,c,d]
      dummyflag = 3;
    } else if (pd == dummypoint) {
      setvertices(fliptets[0], pa, pb, pc, pd);
      dummyflag = 4;
    } else {
      setvertices(fliptets[0], pa, pb, pc, pd);
      if (pp == dummypoint) {
        dummyflag = -1;
      } else {
        dummyflag = 0;
      }
    }
    if (dummyflag > 0) {
      // We deleted 3 hull tets, and create 1 hull tet.
      hullsize -= 2;
    } else if (dummyflag < 0) {
      // We deleted 4 hull tets.
      hullsize -= 4;
      // meshedges does not change.
    }
  } else {
    setvertices(fliptets[0], pa, pb, pc, pd);
  }

  if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
    REAL volneg[4], volpos[1], vol_diff;
    if (dummyflag > 0) {
      if (pa == dummypoint) {
        volneg[0] = 0.;
        volneg[1] = tetprismvol(pp, pd, pb, pc);
        volneg[2] = 0.;
        volneg[3] = 0.;
      } else if (pb == dummypoint) {
        volneg[0] = 0.;
        volneg[1] = 0.;
        volneg[2] = tetprismvol(pp, pd, pc, pa);
        volneg[3] = 0.;
      } else if (pc == dummypoint) {
        volneg[0] = tetprismvol(pp, pd, pa, pb);
        volneg[1] = 0.;
        volneg[2] = 0.;
        volneg[3] = 0.;
      } else { // pd == dummypoint
        volneg[0] = 0.;
        volneg[1] = 0.;
        volneg[2] = 0.;
        volneg[3] = tetprismvol(pa, pb, pc, pp);
      }
      volpos[0] = 0.;
    } else if (dummyflag < 0) {
      volneg[0] = 0.;
      volneg[1] = 0.;
      volneg[2] = 0.;
      volneg[3] = 0.;
      volpos[0] = tetprismvol(pa, pb, pc, pd);
    } else {
      volneg[0] = tetprismvol(pp, pd, pa, pb);
      volneg[1] = tetprismvol(pp, pd, pb, pc);
      volneg[2] = tetprismvol(pp, pd, pc, pa);
      volneg[3] = tetprismvol(pa, pb, pc, pp);
      volpos[0] = tetprismvol(pa, pb, pc, pd);
    }
    vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3];
    fc->tetprism_vol_sum  += vol_diff; // Update the total sum.
  }

  // Bond the new tet to adjacent tets.
  for (i = 0; i < 3; i++) {
    esym(fliptets[0], newface); // At faces [b,a,d], [c,b,d], [a,c,d].
    bond(newface, topcastets[i]);
    enextself(fliptets[0]);
  }
  bond(fliptets[0], botcastet);

  if (checksubsegflag) {
    face checkseg;
    // Bond 6 segments (at edges of [a,b,c,d]) if there there are.
    for (i = 0; i < 3; i++) {
      eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c].
      if (issubseg(newface)) {
        tsspivot1(newface, checkseg);
        esym(fliptets[0], newface);
        enextself(newface); // At edges [a,d], [b,d], [c,d].
        tssbond1(newface, checkseg);
        sstbond1(checkseg, newface);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
      enextself(fliptets[0]);
    }
    for (i = 0; i < 3; i++) {
      if (issubseg(topcastets[i])) {
        tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a].
        tssbond1(fliptets[0], checkseg);
        sstbond1(checkseg, fliptets[0]);
        if (fc->chkencflag & 1) {
          enqueuesubface(badsubsegs, &checkseg);
        }
      }
      enextself(fliptets[0]);
    }
  }

  if (checksubfaceflag) {
    face checksh;
    // Bond 4 subfaces (at faces of [a,b,c,d]) if there are.
    for (i = 0; i < 3; i++) {
      if (issubface(topcastets[i])) {
        tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d]
        esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d]
        sesymself(checksh);
        tsbond(newface, checksh);
        if (fc->chkencflag & 2) {
          enqueuesubface(badsubfacs, &checksh);
        }
      }
      enextself(fliptets[0]);
    }
    if (issubface(botcastet)) {
      tspivot(botcastet, checksh); // At face [b,a,c]
      sesymself(checksh);
      tsbond(fliptets[0], checksh);
      if (fc->chkencflag & 2) {
        enqueuesubface(badsubfacs, &checksh);
      }
    }

    if (spivot >= 0) {
      // Perform a 3-to-1 flip in surface triangulation.
      // Depending on the value of 'spivot', the three subfaces are:
      //   - 0: [a,b,p], [b,d,p], [d,a,p]
      //   - 1: [b,c,p], [c,d,p], [d,b,p] 
      //   - 2: [c,a,p], [a,d,p], [d,c,p] 
      //   - 3: [a,b,p], [b,c,p], [c,a,p]
      // Adjust the three subfaces such that their origins are p, i.e., 
      //   - 3: [p,a,b], [p,b,c], [p,c,a]. (Required by the flip31()).
      for (i = 0; i < 3; i++) {
        senext2self(flipshs[i]);
      }
      flip31(flipshs, 0);
      // Delete the three old subfaces.
      for (i = 0; i < 3; i++) {
        shellfacedealloc(subfaces, flipshs[i].sh);
      }
      if (spivot < 3) {
        // // Bond the new subface to the new tet [a,b,c,d].
        tsbond(topcastets[spivot], flipshs[3]);
        fsym(topcastets[spivot], newface);
        sesym(flipshs[3], checksh);
        tsbond(newface, checksh);
      } else {
        // Bound the new subface [a,b,c] to the new tet [a,b,c,d].
        tsbond(fliptets[0], flipshs[3]);
        fsym(fliptets[0], newface);
        sesym(flipshs[3], checksh);
        tsbond(newface, checksh);
      }
    } // if (spivot > 0)
  } // if (checksubfaceflag)

  if (fc->chkencflag & 4) {
    enqueuetetrahedron(&(fliptets[0]));
  }

  // Update the point-to-tet map.
  setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
  setpoint2tet(pd, (tetrahedron) fliptets[0].tet);

  if (fc->enqflag > 0) {
    // Queue faces which may be locally non-Delaunay.
    flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point).
    if (fc->enqflag > 1) {
      for (i = 0; i < 3; i++) {
        esym(fliptets[0], newface);
        flippush(flipstack, &newface);
        enextself(fliptets[0]);
      }
    }
  }

  recenttet = fliptets[0];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipnm()    Flip an edge through a sequence of elementary flips.          //
//                                                                           //
// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are //
// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,//
// use the right-hand rule.                                                  //
//                                                                           //
// 'level' (>= 0) indicates the current link level. If 'level > 0', we are   //
// flipping a link edge of an edge [a',b'],  and 'abedgepivot' indicates     //
// which link edge, i.e., [c',b'] or [a',c'], is [a,b]  These two parameters //
// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that   //
// do not inside the reduced star of edge [a',b'].                           //
//                                                                           //
// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
// in flipnm([a,b]) so that the mesh is returned to its original state       //
// before doing the flipnm([a,b]) operation.                                 //
//                                                                           //
// The return value is an integer nn, where nn <= n.  If nn is 2, then the   //
// edge is flipped.  The first and the second tets in 'abtets' are new tets. //
// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets  //
// in the current star of [a,b].                                             //
//                                                                           //
// ASSUMPTIONS:                                                              //
//  - Neither a nor b is 'dummypoint'.                                       //
//  - [a,b] must not be a segment.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot,
                       flipconstraints* fc)
{
  triface fliptets[3], spintet, flipedge;
  triface *tmpabtets, *parytet;
  point pa, pb, pc, pd, pe, pf;
  REAL ori;
  int hullflag;
  int reducflag, rejflag;
  int reflexlinkedgecount;
  int edgepivot;
  int n1, nn;
  int t1ver;
  int i, j;

  pa = org(abtets[0]);
  pb = dest(abtets[0]);

  if (n > 3) {
    // Try to reduce the size of the Star(ab) by flipping a face in it. 
    reflexlinkedgecount = 0;

    for (i = 0; i < n; i++) {
      // Let the face of 'abtets[i]' be [a,b,c].
      if (checksubfaceflag) {
        if (issubface(abtets[i])) {
          continue; // Skip a subface.
        }
      }
      // Do not flip this face if it is involved in two Stars.
      if ((elemcounter(abtets[i]) > 1) ||
          (elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
        continue;
      }

      pc = apex(abtets[i]); 
      pd = apex(abtets[(i + 1) % n]);
      pe = apex(abtets[(i - 1 + n) % n]);
      if ((pd == dummypoint) || (pe == dummypoint)) {
        continue; // [a,b,c] is a hull face.
      }


      // Decide whether [a,b,c] is flippable or not.
      reducflag = 0; 

      hullflag = (pc == dummypoint); // pc may be dummypoint.
      if (hullflag == 0) {
        ori = orient3d(pb, pc, pd, pe); // Is [b,c] locally convex?
        if (ori > 0) {
          ori = orient3d(pc, pa, pd, pe); // Is [c,a] locally convex?
          if (ori > 0) {
            // Test if [a,b] is locally convex OR flat.
            ori = orient3d(pa, pb, pd, pe);
            if (ori > 0) {
              // Found a 2-to-3 flip: [a,b,c] => [e,d]
              reducflag = 1;
            } else if (ori == 0) {
              // [a,b] is flat.
              if (n == 4) {
                // The "flat" tet can be removed immedately by a 3-to-2 flip.
                reducflag = 1;
              }
            }
          }
        }
        if (!reducflag) {
          reflexlinkedgecount++;
        }
      } else {
        // 'c' is dummypoint.
        if (n == 4) {
          // Let the vertex opposite to 'c' is 'f'.
          // A 4-to-4 flip is possible if the two tets [d,e,f,a] and [e,d,f,b]
          //   are valid tets. 
          // Note: When the mesh is not convex, it is possible that [a,b] is
          //   locally non-convex (at hull faces [a,b,e] and [b,a,d]).
          //   In this case, an edge flip [a,b] to [e,d] is still possible.
          pf = apex(abtets[(i + 2) % n]);
          assert(pf != dummypoint);
          ori = orient3d(pd, pe, pf, pa);
          if (ori < 0) {
            ori = orient3d(pe, pd, pf, pb);
            if (ori < 0) {
              // Found a 4-to-4 flip: [a,b] => [e,d]
              reducflag = 1;
              ori = 0; // Signal as a 4-to-4 flip (like a co-palanar case).
            }
          }
        }
      } // if (hullflag)

      if (reducflag) {
        // [a,b,c] could be removed by a 2-to-3 flip.
        rejflag = 0;
        if (fc->checkflipeligibility) {
          // Check if the flip can be performed.
          rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level,
                                         abedgepivot, fc);
        }
        if (!rejflag) {
          // Do flip: [a,b,c] => [e,d].
          fliptets[0] = abtets[i];
          fsym(fliptets[0], fliptets[1]); // abtets[i-1].
          flip23(fliptets, hullflag, fc);

          // Shrink the array 'abtets', maintain the original order.
          //   Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])'
          //   are flipped, i.e., they do not in Star(ab) anymore. 
          //   'fliptets[0]' ([e,d,a,b]) is in Star(ab), it is saved in
          //   'abtets[i-1]' (adjust it to be [a,b,e,d]), see below: 
          // 
          //            before                   after
          //     [0] |___________|        [0] |___________| 
          //     ... |___________|        ... |___________|
          //   [i-1] |_[a,b,e,c]_|      [i-1] |_[a,b,e,d]_|
          //     [i] |_[a,b,c,d]_| -->    [i] |_[a,b,d,#]_|
          //   [i+1] |_[a,b,d,#]_|      [i+1] |_[a,b,#,*]_|
          //     ... |___________|        ... |___________|
          //   [n-2] |___________|      [n-2] |___________| 
          //   [n-1] |___________|      [n-1] |_[i]_2-t-3_|
          //
          edestoppoself(fliptets[0]); // [a,b,e,d]
          // Increase the counter of this new tet (it is in Star(ab)).
          increaseelemcounter(fliptets[0]); 
          abtets[(i - 1 + n) % n] = fliptets[0];
          for (j = i; j < n - 1; j++) {
            abtets[j] = abtets[j + 1];  // Upshift
          }
          // The last entry 'abtets[n-1]' is empty. It is used in two ways:
          //   (i) it remebers the vertex 'c' (in 'abtets[n-1].tet'), and
          //  (ii) it remebers the position [i] where this flip took place.
          // These informations let us to either undo this flip or recover
          //   the original edge link (for collecting new created tets).
          //abtets[n - 1] = fliptets[1]; // [e,d,b,c] is remebered.
          abtets[n - 1].tet = (tetrahedron *) pc;
          abtets[n - 1].ver = 0; // Clear it.
          // 'abtets[n - 1].ver' is in range [0,11] -- only uses 4 bits.
          // Use the 5th bit in 'abtets[n - 1].ver' to signal a 2-to-3 flip.
          abtets[n - 1].ver |= (1 << 4);
          // The poisition [i] of this flip is saved above the 7th bit.
          abtets[n - 1].ver |= (i << 6);

          if (fc->collectnewtets) {
            // Push the two new tets [e,d,b,c] and [e,d,c,a] into a stack.
            //   Re-use the global array 'cavetetlist'.
            for (j = 1; j < 3; j++) {
              cavetetlist->newindex((void **) &parytet);
              *parytet = fliptets[j]; // fliptets[1], fliptets[2].
            }
          }

          // Star(ab) is reduced. Try to flip the edge [a,b].
          nn = flipnm(abtets, n - 1, level, abedgepivot, fc);

          if (nn == 2) {
            // The edge has been flipped.
            return nn;
          } else { // if (nn > 2)
            // The edge is not flipped.
            if (fc->unflip || (ori == 0)) {
              // Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to 
              //   transform [e,d] => [a,b,c].
              // 'ori == 0' means that the previous flip created a degenrated
              //   tet. It must be removed. 
              // Remeber that 'abtets[i-1]' is [a,b,e,d]. We can use it to
              //   find another two tets [e,d,b,c] and [e,d,c,a].
              fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d]
              edestoppoself(fliptets[0]); // [e,d,a,b]
              fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c]
              fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a]
              assert(apex(fliptets[0]) == oppo(fliptets[2])); // SELF_CHECK
              // Restore the two original tets in Star(ab). 
              flip32(fliptets, hullflag, fc);
              // Marktest the two restored tets in Star(ab).
              for (j = 0; j < 2; j++) {
                increaseelemcounter(fliptets[j]);
              }
              // Expand the array 'abtets', maintain the original order.
              for (j = n - 2; j>= i; j--) {
                abtets[j + 1] = abtets[j];  // Downshift
              }
              // Insert the two new tets 'fliptets[0]' [a,b,c,d] and 
              //  'fliptets[1]' [b,a,c,e] into the (i-1)-th and i-th entries, 
              //  respectively.
              esym(fliptets[1], abtets[(i - 1 + n) % n]); // [a,b,e,c]
              abtets[i] = fliptets[0]; // [a,b,c,d]
              nn++;
              if (fc->collectnewtets) {
                // Pop two (flipped) tets from the stack.
                cavetetlist->objects -= 2;
              }
            } // if (upflip || (ori == 0))
          } // if (nn > 2)

          if (!fc->unflip) {
            // The flips are not reversed. The current Star(ab) can not be
            //   further reduced. Return its current size (# of tets).
            return nn; 
          }
          // unflip is set. 
          // Continue the search for flips.
        }
      } // if (reducflag)
    } // i

    // The Star(ab) is not reduced. 
    if (reflexlinkedgecount > 0) {
      // There are reflex edges in the Link(ab).
      if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || 
          ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) {
        // Try to reduce the Star(ab) by flipping a reflex edge in Link(ab).
        for (i = 0; i < n; i++) {
          // Do not flip this face [a,b,c] if there are two Stars involved.
          if ((elemcounter(abtets[i]) > 1) ||
              (elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
            continue;
          }
          pc = apex(abtets[i]);
          if (pc == dummypoint) {
            continue; // [a,b] is a hull edge.
          }
          pd = apex(abtets[(i + 1) % n]);
          pe = apex(abtets[(i - 1 + n) % n]);
          if ((pd == dummypoint) || (pe == dummypoint)) {
            continue; // [a,b,c] is a hull face.
          }


          edgepivot = 0; // No edge is selected yet.

          // Test if [b,c] is locally convex or flat.
          ori = orient3d(pb, pc, pd, pe);
          if (ori <= 0) {
            // Select the edge [c,b].
            enext(abtets[i], flipedge); // [b,c,a,d]
            edgepivot = 1;
          }
          if (!edgepivot) {
            // Test if [c,a] is locally convex or flat.
            ori = orient3d(pc, pa, pd, pe);
            if (ori <= 0) {
              // Select the edge [a,c].
              eprev(abtets[i], flipedge); // [c,a,b,d].
              edgepivot = 2;
            }
          }

          if (!edgepivot) continue;

          // An edge is selected.
          if (checksubsegflag) {
            // Do not flip it if it is a segment.
            if (issubseg(flipedge)) {
              if (fc->collectencsegflag) {
                face checkseg, *paryseg;
                tsspivot1(flipedge, checkseg);
                if (!sinfected(checkseg)) {
                  // Queue this segment in list.
                  sinfect(checkseg);                
                  caveencseglist->newindex((void **) &paryseg);
                  *paryseg = checkseg;
                }
              }
              continue;
            }
          }

          // Try to flip the selected edge ([c,b] or [a,c]).
          esymself(flipedge); 
          // Count the number of tets at the edge.
          n1 = 0;
          j = 0; // Sum of the star counters.
          spintet = flipedge;
          while (1) {
            n1++;
            j += (elemcounter(spintet)); 
            fnextself(spintet);
            if (spintet.tet == flipedge.tet) break;
          }
          assert(n1 >= 3);
          if (j > 2) {
            // The Star(flipedge) overlaps other Stars.
            continue; // Do not flip this edge.
          }
          // Only two tets can be marktested.
          assert(j == 2); 

          if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) {
            // The star size exceeds the given limit.
            continue; // Do not flip it.
          }

          // Allocate spaces for Star(flipedge).
          tmpabtets = new triface[n1];
          // Form the Star(flipedge).
          j = 0;
          spintet = flipedge;
          while (1) {
            tmpabtets[j] = spintet;
            // Increase the star counter of this tet.
            increaseelemcounter(tmpabtets[j]); 
            j++;
            fnextself(spintet);
            if (spintet.tet == flipedge.tet) break;
          }

          // Try to flip the selected edge away.
          nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc);

          if (nn == 2) {
            // The edge is flipped. Star(ab) is reduced.
            // Shrink the array 'abtets', maintain the original order.
            if (edgepivot == 1) {
              // 'tmpabtets[0]' is [d,a,e,b] => contains [a,b].
              spintet = tmpabtets[0]; // [d,a,e,b]
              enextself(spintet);
              esymself(spintet);
              enextself(spintet); // [a,b,e,d]
            } else {
              // 'tmpabtets[1]' is [b,d,e,a] => contains [a,b].
              spintet = tmpabtets[1]; // [b,d,e,a]
              eprevself(spintet);
              esymself(spintet);
              eprevself(spintet); // [a,b,e,d]
            } // edgepivot == 2
            assert(elemcounter(spintet) == 0); // It's a new tet.
            increaseelemcounter(spintet); // It is in Star(ab).
            // Put the new tet at [i-1]-th entry.
            abtets[(i - 1 + n) % n] = spintet;
            for (j = i; j < n - 1; j++) {
              abtets[j] = abtets[j + 1];  // Upshift
            }
            // Remember the flips in the last entry of the array 'abtets'.
            // They can be used to recover the flipped edge.
            abtets[n - 1].tet = (tetrahedron *) tmpabtets; // The star(fedge).
            abtets[n - 1].ver = 0; // Clear it.
            // Use the 1st and 2nd bit to save 'edgepivot' (1 or 2).
            abtets[n - 1].ver |= edgepivot;
            // Use the 6th bit to signal this n1-to-m1 flip.
            abtets[n - 1].ver |= (1 << 5); 
            // The poisition [i] of this flip is saved from 7th to 19th bit.
            abtets[n - 1].ver |= (i << 6);
            // The size of the star 'n1' is saved from 20th bit.
            abtets[n - 1].ver |= (n1 << 19);

            // Remember the flipped link vertex 'c'. It can be used to recover
            //   the original edge link of [a,b], and to collect new tets.
            tmpabtets[0].tet = (tetrahedron *) pc;
            tmpabtets[0].ver = (1 << 5); // Flag it as a vertex handle.

            // Continue to flip the edge [a,b].
            nn = flipnm(abtets, n - 1, level, abedgepivot, fc);

            if (nn == 2) { 
              // The edge has been flipped.
              return nn;
            } else { // if (nn > 2) {
              // The edge is not flipped.
              if (fc->unflip) {
                // Recover the flipped edge ([c,b] or [a,c]).
                assert(nn == (n - 1));
                // The sequence of flips are saved in 'tmpabtets'. 
                // abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by
                //   the flipping of edge [c,b] or [a,c].It must still exist in
                //   Star(ab). It is the start tet to recover the flipped edge.
                if (edgepivot == 1) { 
                  // The flip edge is [c,b].
                  tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
                  eprevself(tmpabtets[0]);
                  esymself(tmpabtets[0]);
                  eprevself(tmpabtets[0]); // [d,a,e,b]
                  fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
                } else {
                  // The flip edge is [a,c].
                  tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
                  enextself(tmpabtets[1]);
                  esymself(tmpabtets[1]);
                  enextself(tmpabtets[1]); // [b,d,e,a]
                  fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
                } // if (edgepivot == 2)

                // Recover the flipped edge ([c,b] or [a,c]).
                flipnm_post(tmpabtets, n1, 2, edgepivot, fc);

                // Insert the two recovered tets into Star(ab).
                for (j = n - 2; j >= i; j--) {
                  abtets[j + 1] = abtets[j];  // Downshift
                }
                if (edgepivot == 1) {
                  // tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
                  // tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
                  // tmpabtets[2] is [c,b,e,d]
                  fliptets[0] = tmpabtets[1];
                  enextself(fliptets[0]);
                  esymself(fliptets[0]); // [a,b,e,c]
                  fliptets[1] = tmpabtets[0];
                  esymself(fliptets[1]);
                  eprevself(fliptets[1]); // [a,b,c,d]
                } else {
                  // tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
                  // tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
                  // tmpabtets[2] is [a,c,e,d]
                  fliptets[0] = tmpabtets[1];
                  eprevself(fliptets[0]);
                  esymself(fliptets[0]); // [a,b,e,c]
                  fliptets[1] = tmpabtets[0];
                  esymself(fliptets[1]);
                  enextself(fliptets[1]); // [a,b,c,d]
                } // edgepivot == 2
                for (j = 0; j < 2; j++) {
                  assert(elemcounter(fliptets[j]) == 0); // SELF_CHECK
                  increaseelemcounter(fliptets[j]);
                }
                // Insert the two recovered tets into Star(ab).
                abtets[(i - 1 + n) % n] = fliptets[0];
                abtets[i] = fliptets[1];
                nn++;
                // Release the allocated spaces.
                delete [] tmpabtets;
              } // if (unflip)
            } // if (nn > 2)

            if (!fc->unflip) {
              // The flips are not reversed. The current Star(ab) can not be
              //   further reduced. Return its size (# of tets).
              return nn; 
            }
            // unflip is set. 
            // Continue the search for flips.
          } else {
            // The seclected edge is not flipped.
            if (fc->unflip) {
              // The memory should already be freed.
              assert(nn == n1);
            } else {
              // Release the memory used in this attempted flip.
              flipnm_post(tmpabtets, n1, nn, edgepivot, fc);
            }
            // Decrease the star counters of tets in Star(flipedge).
            for (j = 0; j < nn; j++) {
              assert(elemcounter(tmpabtets[j]) > 0); // SELF_CHECK
              decreaseelemcounter(tmpabtets[j]);
            }
            // Release the allocated spaces.
            delete [] tmpabtets;
          }
        } // i
      } // if (level...)
    } // if (reflexlinkedgecount > 0)
  } else {
    // Check if a 3-to-2 flip is possible.
    // Let the three apexes be c, d,and e. Hull tets may be involved. If so, 
    //   we rearrange them such that the vertex e is dummypoint. 
    hullflag = 0;

    if (apex(abtets[0]) == dummypoint) {
      pc = apex(abtets[1]);
      pd = apex(abtets[2]);
      pe = apex(abtets[0]);
      hullflag = 1;
    } else if (apex(abtets[1]) == dummypoint) {
      pc = apex(abtets[2]);
      pd = apex(abtets[0]);
      pe = apex(abtets[1]);
      hullflag = 2;
    } else {
      pc = apex(abtets[0]);
      pd = apex(abtets[1]);
      pe = apex(abtets[2]);
      hullflag = (pe == dummypoint) ? 3 : 0;
    }

    reducflag = 0;
    rejflag = 0;


    if (hullflag == 0) {
      // Make sure that no inverted tet will be created, i.e. the new tets
      //   [d,c,e,a] and [c,d,e,b] must be valid tets. 
      ori = orient3d(pd, pc, pe, pa);
      if (ori < 0) {
        ori = orient3d(pc, pd, pe, pb);
        if (ori < 0) {
          reducflag = 1;
        }
      }
    } else {
      // [a,b] is a hull edge.
      //   Note: This can happen when it is in the middle of a 4-to-4 flip.
      //   Note: [a,b] may even be a non-convex hull edge.
      if (!nonconvex) {
        //  The mesh is convex, only do flip if it is a coplanar hull edge.
        ori = orient3d(pa, pb, pc, pd); 
        if (ori == 0) {
          reducflag = 1;
        }
      } else { // nonconvex
        reducflag = 1;
      }
      if (reducflag == 1) {
        // [a,b], [a,b,c] and [a,b,d] are on the convex hull.
        // Make sure that no inverted tet will be created.
        point searchpt = nullptr, chkpt;
        REAL bigvol = 0.0, ori1, ori2;
        // Search an interior vertex which is an apex of edge [c,d].
        //   In principle, it can be arbitray interior vertex.  To avoid
        //   numerical issue, we choose the vertex which belongs to a tet
        //   't' at edge [c,d] and 't' has the biggest volume.  
        fliptets[0] = abtets[hullflag % 3]; // [a,b,c,d].
        eorgoppoself(fliptets[0]);  // [d,c,b,a]
        spintet = fliptets[0];
        while (1) {
          fnextself(spintet);
          chkpt = oppo(spintet);
          if (chkpt == pb) break;
          if ((chkpt != dummypoint) && (apex(spintet) != dummypoint)) {
            ori = -orient3d(pd, pc, apex(spintet), chkpt);
            assert(ori > 0);
            if (ori > bigvol) {
              bigvol = ori;
              searchpt = chkpt;
            }
          }
        }
        if (searchpt != nullptr) { 
          // Now valid the configuration.
          ori1 = orient3d(pd, pc, searchpt, pa);
          ori2 = orient3d(pd, pc, searchpt, pb);
          if (ori1 * ori2 >= 0.0) {
            reducflag = 0; // Not valid. 
          } else {
            ori1 = orient3d(pa, pb, searchpt, pc);
            ori2 = orient3d(pa, pb, searchpt, pd);
            if (ori1 * ori2 >= 0.0) {
              reducflag = 0; // Not valid.
            }
          }
        } else {
          // No valid searchpt is found.
          reducflag = 0; // Do not flip it.
        }
      } // if (reducflag == 1)
    } // if (hullflag == 1)

    if (reducflag) {
      // A 3-to-2 flip is possible.
      if (checksubfaceflag) {
        // This edge (must not be a segment) can be flipped ONLY IF it belongs
        //   to either 0 or 2 subfaces.  In the latter case, a 2-to-2 flip in 
        //   the surface mesh will be automatically performed within the 
        //   3-to-2 flip.
        nn = 0;
        edgepivot = -1; // Re-use it.
        for (j = 0; j < 3; j++) {
          if (issubface(abtets[j])) {
            nn++; // Found a subface.
          } else {
            edgepivot = j;
          }
        }
        assert(nn < 3);
        if (nn == 1) {
          // Found only 1 subface containing this edge. This can happen in 
          //   the boundary recovery phase. The neighbor subface is not yet 
          //   recovered. This edge should not be flipped at this moment.
          rejflag = 1; 
        } else if (nn == 2) {
          // Found two subfaces. A 2-to-2 flip is possible. Validate it.
          // Below we check if the two faces [p,q,a] and [p,q,b] are subfaces.
          eorgoppo(abtets[(edgepivot + 1) % 3], spintet); // [q,p,b,a]
          if (issubface(spintet)) {
            rejflag = 1; // Conflict to a 2-to-2 flip.
          } else {
            esymself(spintet);
            if (issubface(spintet)) {
              rejflag = 1; // Conflict to a 2-to-2 flip.
            }
          }
        }
      }
      if (!rejflag && fc->checkflipeligibility) {
        // Here we must exchange 'a' and 'b'. Since in the check... function,
        //   we assume the following point sequence, 'a,b,c,d,e', where
        //   the face [a,b,c] will be flipped and the edge [e,d] will be
        //   created. The two new tets are [a,b,c,d] and [b,a,c,e]. 
        rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, 
                                       abedgepivot, fc);
      }
      if (!rejflag) {
        // Do flip: [a,b] => [c,d,e]
        flip32(abtets, hullflag, fc);
        if (fc->remove_ndelaunay_edge) {
          if (level == 0) {
            // It is the desired removing edge. Check if we have improved
            //   the objective function.
            if ((fc->tetprism_vol_sum >= 0.0) ||
                (fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) {
              // No improvement! flip back: [c,d,e] => [a,b].
              flip23(abtets, hullflag, fc);
              // Increase the element counter -- They are in cavity.
              for (j = 0; j < 3; j++) {
                increaseelemcounter(abtets[j]); 
              }
              return 3;
            }
          } // if (level == 0)
        }
        if (fc->collectnewtets) {
          // Collect new tets.
          if (level == 0) {
            // Push the two new tets into stack.
            for (j = 0; j < 2; j++) {
              cavetetlist->newindex((void **) &parytet);
              *parytet = abtets[j];
            }
          } else {
            // Only one of the new tets is collected. The other one is inside
            //   the reduced edge star. 'abedgepivot' is either '1' or '2'.
            cavetetlist->newindex((void **) &parytet);
            if (abedgepivot == 1) { // [c,b]
              *parytet = abtets[1];
            } else { 
              assert(abedgepivot == 2); // [a,c]
              *parytet = abtets[0];
            }
          }
        } // if (fc->collectnewtets)
        return 2;
      }
    } // if (reducflag)
  } // if (n == 3)

  // The current (reduced) Star size.
  return n;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipnm_post()    Post process a n-to-m flip.                              //
//                                                                           //
// IMPORTANT: This routine only works when there is no other flip operation  //
// is done after flipnm([a,b]) which attempts to remove an edge [a,b].       //
//                                                                           //
// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of //
// [a,b] before flipnm([a,b]).  'nn' (< n) is the value returned by flipnm.  //
// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'//
// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, //
// b] and its initial Star([a,b]).  If 'nn >= 3' edge [a,b] still exists in  //
// current mesh and 'nn' is the current number of tets in Star([a,b]).       //
//                                                                           //
// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a      //
// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet        //
// 'abtets[t-1]', where '0 <= t <= i'.  These information can be used to     //
// undo the flips performed in flipnm([a,b]) or to collect new tets created  //
// by the flipnm([a,b]) operation.                                           //
//                                                                           //
// Default, this routine only walks through the flips and frees the spaces   //
// allocated during the flipnm([a,b]) operation.                             //
//                                                                           //
// If the flag 'fc->unflip' is set, this routine un-does the flips performed //
// in flipnm([a,b]) so that the mesh is returned to its original state       //
// before doing the flipnm([a,b]) operation.                                 //
//                                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot,
                            flipconstraints* fc)
{
  triface fliptets[3], flipface;
  triface *tmpabtets;
  int fliptype;
  int edgepivot;
  int t, n1;
  int i, j;


  if (nn == 2) {
    // The edge [a,b] has been flipped.
    // 'abtets[0]' is [c,d,e,b] or [#,#,#,b].
    // 'abtets[1]' is [d,c,e,a] or [#,#,#,a].
    if (fc->unflip) {
      // Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets.
      flip23(abtets, 1, fc);
      if (fc->collectnewtets) {
        // Pop up new (flipped) tets from the stack.
        if (abedgepivot == 0) {
          // Two new tets were collected.
          cavetetlist->objects -= 2;
        } else {
          // Only one of the two new tets was collected.
          cavetetlist->objects -= 1;
        }
      }
    } 
    // The initial size of Star(ab) is 3.
    nn++;
  } 

  // Walk through the performed flips.
  for (i = nn; i < n; i++) {
    // At the beginning of each step 'i', the size of the Star([a,b]) is 'i'.
    // At the end of this step, the size of the Star([a,b]) is 'i+1'.
    // The sizes of the Link([a,b]) are the same.
    fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2.
    if (fliptype == 1) {
      // It was a 2-to-3 flip: [a,b,c]->[e,d].
      t = (abtets[i].ver >> 6);
      assert(t <= i);
      if (fc->unflip) {
        if (b->verbose > 2) {
          printf("      Recover a 2-to-3 flip at f[%d].\n", t);
        }
        // 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e.,
        //   it is created by a 2-to-3 flip [a,b,c] => [e,d].
        fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d]
        eprevself(fliptets[0]);
        esymself(fliptets[0]);
        enextself(fliptets[0]); // [e,d,a,b]
        fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
        fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
        // Do a 3-to-2 flip: [e,d] => [a,b,c].
        // NOTE: hull tets may be invloved.
        flip32(fliptets, 1, fc);
        // Expand the array 'abtets', maintain the original order.
        // The new array length is (i+1).
        for (j = i - 1; j >= t; j--) {
          abtets[j + 1] = abtets[j];  // Downshift
        }
        // The tet abtets[(t-1)%i] is deleted. Insert the two new tets 
        //   'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into
        //   the (t-1)-th and t-th entries, respectively.
        esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c]
        abtets[t] = fliptets[0]; // [a,b,c,d]
        if (fc->collectnewtets) {
          // Pop up two (flipped) tets from the stack.
          cavetetlist->objects -= 2;
        }
      } 
    } else if (fliptype == 2) {
      tmpabtets = (triface *) (abtets[i].tet);
      n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191
      edgepivot = (abtets[i].ver & 3); 
      t = ((abtets[i].ver >> 6) & 8191);
      assert(t <= i);
      if (fc->unflip) {        
        if (b->verbose > 2) {
          printf("      Recover a %d-to-m flip at e[%d] of f[%d].\n", n1, 
                 edgepivot, t);
        }
        // Recover the flipped edge ([c,b] or [a,c]).
        // abtets[(t - 1 + i) % i] is [a,b,e,d], i.e., the tet created by
        //   the flipping of edge [c,b] or [a,c]. It must still exist in
        //   Star(ab). Use it to recover the flipped edge.
        if (edgepivot == 1) { 
          // The flip edge is [c,b].
          tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
          eprevself(tmpabtets[0]);
          esymself(tmpabtets[0]);
          eprevself(tmpabtets[0]); // [d,a,e,b]
          fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
        } else {
          // The flip edge is [a,c].
          tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
          enextself(tmpabtets[1]);
          esymself(tmpabtets[1]);
          enextself(tmpabtets[1]); // [b,d,e,a]
          fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
        } // if (edgepivot == 2)

        // Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]).
        flipnm_post(tmpabtets, n1, 2, edgepivot, fc);

        // Insert the two recovered tets into the original Star(ab).
        for (j = i - 1; j >= t; j--) {
          abtets[j + 1] = abtets[j];  // Downshift
        }
        if (edgepivot == 1) {
          // tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
          // tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
          // tmpabtets[2] is [c,b,e,d]
          fliptets[0] = tmpabtets[1];
          enextself(fliptets[0]);
          esymself(fliptets[0]); // [a,b,e,c]
          fliptets[1] = tmpabtets[0];
          esymself(fliptets[1]);
          eprevself(fliptets[1]); // [a,b,c,d]
        } else {
          // tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
          // tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
          // tmpabtets[2] is [a,c,e,d]
          fliptets[0] = tmpabtets[1];
          eprevself(fliptets[0]);
          esymself(fliptets[0]); // [a,b,e,c]
          fliptets[1] = tmpabtets[0];
          esymself(fliptets[1]);
          enextself(fliptets[1]); // [a,b,c,d]
        } // edgepivot == 2
        // Insert the two recovered tets into Star(ab).
        abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0];
        abtets[t] = fliptets[1];
      } 
      else {
        // Only free the spaces.
        flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
      } // if (!unflip)
      if (b->verbose > 2) {
        printf("      Release %d spaces at f[%d].\n", n1, i);
      }
      delete [] tmpabtets;
    }
  } // i

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertpoint()    Insert a point into current tetrahedralization.          //
//                                                                           //
// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the   //
// tetrahedralization T. It first finds a "cavity", denoted as C, in T,  C   //
// consists of tetrahedra in T that "conflict" with p.  If T is a Delaunay   //
// tetrahedralization, then all boundary faces (triangles) of C are visible  //
// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all //
// tetrahedra in C, then creating new tetrahedra formed by boundary faces of //
// C and p.  If T is not a DT, then C may be not star-shaped.  It must be    //
// modified so that it becomes star-shaped.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh,
                            face *splitseg, insertvertexflags *ivf)
{
  arraypool *swaplist;
  triface *cavetet, spintet, neightet, neineitet, *parytet;
  triface oldtet, newtet, newneitet;
  face checksh, neighsh, *parysh;
  face checkseg, *paryseg;
  point *pts, pa, pb, pc, *parypt;
  enum locateresult loc = OUTSIDE;
  REAL sign, ori;
  REAL attrib, volume;
  bool enqflag;
  int t1ver;
  int i, j, k, s;

  if (b->verbose > 2) {
    printf("      Insert point %d\n", pointmark(insertpt));
  }

  // Locate the point.
  if (searchtet->tet != nullptr) {
    loc = (enum locateresult) ivf->iloc;
  }

  if (loc == OUTSIDE) {
    if (searchtet->tet == nullptr) {
      if (!b->weighted) {
        randomsample(insertpt, searchtet);
      } else {
        // Weighted DT. There may exist dangling vertex. 
        *searchtet = recenttet;
      }
    }
    // Locate the point.
    loc = locate(insertpt, searchtet, ivf->chkencflag); 
  }

  ivf->iloc = (int) loc; // The return value.

  if (b->weighted) {
    if (loc != OUTSIDE) {
      // Check if this vertex is regular.
      pts = (point *) searchtet->tet;
      assert(pts[7] != dummypoint);
      sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt,
                        pts[4][3], pts[5][3], pts[6][3], pts[7][3],
                        insertpt[3]);
      if (sign > 0) {
        // This new vertex does not lie below the lower hull. Skip it.
        setpointtype(insertpt, NREGULARVERTEX);
        nonregularcount++;
        ivf->iloc = (int) NONREGULAR;
        return 0;
      }
    }
  }

  // Create the initial cavity C(p) which contains all tetrahedra that
  //   intersect p. It may include 1, 2, or n tetrahedra.
  // If p lies on a segment or subface, also create the initial sub-cavity
  //   sC(p) which contains all subfaces (and segment) which intersect p.

  if (loc == OUTSIDE) {
    flip14count++;
    // The current hull will be enlarged.
    // Add four adjacent boundary tets into list.
    for (i = 0; i < 4; i++) {
      decode(searchtet->tet[i], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    infect(*searchtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = *searchtet;
  } else if (loc == INTETRAHEDRON) {
    flip14count++;
    // Add four adjacent boundary tets into list.
    for (i = 0; i < 4; i++) {
      decode(searchtet->tet[i], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    infect(*searchtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = *searchtet;
  } else if (loc == ONFACE) {
    flip26count++;
    // Add six adjacent boundary tets into list.
    j = (searchtet->ver & 3); // The current face number.
    for (i = 1; i < 4; i++) { 
      decode(searchtet->tet[(j + i) % 4], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    decode(searchtet->tet[j], spintet);
    j = (spintet.ver & 3); // The current face number.
    for (i = 1; i < 4; i++) {
      decode(spintet.tet[(j + i) % 4], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    infect(spintet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = spintet;
    infect(*searchtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = *searchtet;

    if (ivf->splitbdflag) { 
      if ((splitsh != nullptr) && (splitsh->sh != nullptr)) {
        // Create the initial sub-cavity sC(p).
        smarktest(*splitsh);
        caveshlist->newindex((void **) &parysh);
        *parysh = *splitsh;
      }
    } // if (splitbdflag)
  } else if (loc == ONEDGE) {
    flipn2ncount++;
    // Add all adjacent boundary tets into list.
    spintet = *searchtet;
    while (1) {
      eorgoppo(spintet, neightet);
      decode(neightet.tet[neightet.ver & 3], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
      edestoppo(spintet, neightet);
      decode(neightet.tet[neightet.ver & 3], neightet);
      neightet.ver = epivot[neightet.ver];
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
      infect(spintet);
      caveoldtetlist->newindex((void **) &parytet);
      *parytet = spintet;
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    } // while (1)

    if (ivf->splitbdflag) {
      // Create the initial sub-cavity sC(p).
      if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
        smarktest(*splitseg);
        splitseg->shver = 0;
        spivot(*splitseg, *splitsh);
      }
      if (splitsh != nullptr) {
        if (splitsh->sh != nullptr) {
          // Collect all subfaces share at this edge.
          pa = sorg(*splitsh);
          neighsh = *splitsh;
          while (1) {
            // Adjust the origin of its edge to be 'pa'.
            if (sorg(neighsh) != pa) {
              sesymself(neighsh);
            }
            // Add this face into list (in B-W cavity).
            smarktest(neighsh);
            caveshlist->newindex((void **) &parysh);
            *parysh = neighsh;
            // Add this face into face-at-splitedge list.
            cavesegshlist->newindex((void **) &parysh);
            *parysh = neighsh;
            // Go to the next face at the edge.
            spivotself(neighsh);
            // Stop if all faces at the edge have been visited.
            if (neighsh.sh == splitsh->sh) break;
            if (neighsh.sh == nullptr) break;
          } // while (1)
        } // if (not a dangling segment)
      }
    } // if (splitbdflag)
  } else if (loc == INSTAR) {
    // We assume that all tets in the star are given in 'caveoldtetlist',
    //   and they are all infected.
    assert(caveoldtetlist->objects > 0);
    // Collect the boundary faces of the star.
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      // Check its 4 neighbor tets.
      for (j = 0; j < 4; j++) {
        decode(cavetet->tet[j], neightet);
        if (!infected(neightet)) {
          // It's a boundary face.
          neightet.ver = epivot[neightet.ver];
          cavebdrylist->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    }
  } else if (loc == ONVERTEX) {
    // The point already exist. Do nothing and return.
    return 0;
  } else if (loc == ENCSUBFACE) {
    // Treat it as outside.
    ivf->iloc = (int) OUTSIDE;
    return 0;
  }


  if (ivf->assignmeshsize) {
    // Assign mesh size for the new point.
    if (bgm != nullptr) {
      // Interpolate the mesh size from the background mesh. 
      bgm->decode(point2bgmtet(org(*searchtet)), neightet);
      int bgmloc = (int) bgm->scoutpoint(insertpt, &neightet, 0);
      if (bgmloc != (int) OUTSIDE) {
        insertpt[pointmtrindex] =  
          bgm->getpointmeshsize(insertpt, &neightet, bgmloc);
        setpoint2bgmtet(insertpt, bgm->encode(neightet));
      }
    } else {
      insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc);
    }
  } // if (assignmeshsize)

  if (ivf->bowywat) {
    // Update the cavity C(p) using the Bowyer-Watson algorithm.
    swaplist = cavetetlist;
    cavetetlist = cavebdrylist;
    cavebdrylist = swaplist;
    for (i = 0; i < cavetetlist->objects; i++) {
      // 'cavetet' is an adjacent tet at outside of the cavity.
      cavetet = (triface *) fastlookup(cavetetlist, i);
      // The tet may be tested and included in the (enlarged) cavity.
      if (!infected(*cavetet)) {
        // Check for two possible cases for this tet: 
        //   (1) It is a cavity tet, or
        //   (2) it is a cavity boundary face.
        enqflag = false;
        if (!marktested(*cavetet)) {
          // Do Delaunay (in-sphere) test.
          pts = (point *) cavetet->tet;
          if (pts[7] != dummypoint) {
            // A volume tet. Operate on it.
            if (b->weighted) {
              sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt,
                                pts[4][3], pts[5][3], pts[6][3], pts[7][3],
                                insertpt[3]);
            } else {
              sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt);
            }
            enqflag = (sign < 0.0);
          } else {
            if (!nonconvex) {
              // Test if this hull face is visible by the new point. 
              ori = orient3d(pts[4], pts[5], pts[6], insertpt); 
              if (ori < 0) {
                // A visible hull face. 
                //if (!nonconvex) { 
                // Include it in the cavity. The convex hull will be enlarged.
                enqflag = true; // (ori < 0.0);
                        //}
              } else if (ori == 0.0) {
                // A coplanar hull face. We need to test if this hull face is
                //   Delaunay or not. We test if the adjacent tet (not faked)
                //   of this hull face is Delaunay or not.
                decode(cavetet->tet[3], neineitet);
                if (!infected(neineitet)) {
                  if (!marktested(neineitet)) {
                    // Do Delaunay test on this tet.
                    pts = (point *) neineitet.tet;
                    assert(pts[7] != dummypoint);
                    if (b->weighted) {
                      sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
                                        pts[4][3], pts[5][3], pts[6][3], 
                                        pts[7][3], insertpt[3]);
                    } else {
                      sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
                    }
                    enqflag = (sign < 0.0);
                  } 
                } else {
                  // The adjacent tet is non-Delaunay. The hull face is non-
                  //   Delaunay as well. Include it in the cavity.
                  enqflag = true;
                } // if (!infected(neineitet))
              } // if (ori == 0.0)
            } else {
              // A hull face (must be a subface).
              // We FIRST include it in the initial cavity if the adjacent tet
              //   (not faked) of this hull face is not Delaunay wrt p.
              //   Whether it belongs to the final cavity will be determined
              //   during the validation process. 'validflag'.
              decode(cavetet->tet[3], neineitet);
              if (!infected(neineitet)) {
                if (!marktested(neineitet)) {
                  // Do Delaunay test on this tet.
                  pts = (point *) neineitet.tet;
                  assert(pts[7] != dummypoint);
                  if (b->weighted) {
                    sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt,
                                      pts[4][3], pts[5][3], pts[6][3], 
                                      pts[7][3], insertpt[3]);
                  } else {
                    sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
                  }
                  enqflag = (sign < 0.0);
                } 
              } else {
                // The adjacent tet is non-Delaunay. The hull face is non-
                //   Delaunay as well. Include it in the cavity.
                enqflag = true;
              } // if (infected(neineitet))
            } // if (nonconvex)
          } // if (pts[7] != dummypoint)
          marktest(*cavetet); // Only test it once.
        } // if (!marktested(*cavetet))

        if (enqflag) {
          // Found a tet in the cavity. Put other three faces in check list.
          k = (cavetet->ver & 3); // The current face number
          for (j = 1; j < 4; j++) {
            decode(cavetet->tet[(j + k) % 4], neightet);
            cavetetlist->newindex((void **) &parytet);
            *parytet = neightet;
          }
          infect(*cavetet);
          caveoldtetlist->newindex((void **) &parytet);
          *parytet = *cavetet;
        } else {
          // Found a boundary face of the cavity. 
          cavetet->ver = epivot[cavetet->ver];
          cavebdrylist->newindex((void **) &parytet);
          *parytet = *cavetet;
        }
      } // if (!infected(*cavetet))
    } // i

    cavetetlist->restart(); // Clear the working list.
  } // if (ivf->bowywat)

  if (checksubsegflag) {
    // Collect all segments of C(p).
    shellface *ssptr;
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      if ((ssptr = (shellface*) cavetet->tet[8]) != nullptr) {
        for (j = 0; j < 6; j++) {
          if (ssptr[j]) {
            sdecode(ssptr[j], checkseg);
            if (!sinfected(checkseg)) {
              sinfect(checkseg);
              cavetetseglist->newindex((void **) &paryseg);
              *paryseg = checkseg;
            }
          }
        } // j
      }
    } // i
    // Uninfect collected segments.
    for (i = 0; i < cavetetseglist->objects; i++) {
      paryseg = (face *) fastlookup(cavetetseglist, i);
      suninfect(*paryseg);
    }

    if (ivf->rejflag & 1) {
      // Reject this point if it encroaches upon any segment.
      face *paryseg1;
      for (i = 0; i < cavetetseglist->objects; i++) {
        paryseg1 = (face *) fastlookup(cavetetseglist, i);
        if (checkseg4encroach((point) paryseg1->sh[3], (point) paryseg1->sh[4], 
                              insertpt)) {
          encseglist->newindex((void **) &paryseg);
          *paryseg = *paryseg1;
        }
      } // i
      if (encseglist->objects > 0) {
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) ENCSEGMENT;
        return 0;
      }
    }
  } // if (checksubsegflag)

  if (checksubfaceflag) {
    // Collect all subfaces of C(p).
    shellface *sptr;
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      if ((sptr = (shellface*) cavetet->tet[9]) != nullptr) {
        for (j = 0; j < 4; j++) {
          if (sptr[j]) {
            sdecode(sptr[j], checksh);
            if (!sinfected(checksh)) {
              sinfect(checksh);
              cavetetshlist->newindex((void **) &parysh);
              *parysh = checksh;
            }
          }
        } // j
      }
    } // i
    // Uninfect collected subfaces.
    for (i = 0; i < cavetetshlist->objects; i++) {
      parysh = (face *) fastlookup(cavetetshlist, i);
      suninfect(*parysh);
    }

    if (ivf->rejflag & 2) {
      REAL rd, cent[3];
      badface *bface;
      // Reject this point if it encroaches upon any subface.
      for (i = 0; i < cavetetshlist->objects; i++) {
        parysh = (face *) fastlookup(cavetetshlist, i);
        if (checkfac4encroach((point) parysh->sh[3], (point) parysh->sh[4], 
                              (point) parysh->sh[5], insertpt, cent, &rd)) {
          encshlist->newindex((void **) &bface);
          bface->ss = *parysh;
          bface->forg = (point) parysh->sh[3]; // Not a dad one.
          for (j = 0; j < 3; j++) bface->cent[j] = cent[j];
          bface->key = rd;
        }
      }
      if (encshlist->objects > 0) {
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) ENCSUBFACE;
        return 0;
      }
    }
  } // if (checksubfaceflag)

  if (ivf->splitbdflag) { 
    // The new point locates in surface mesh. Update the sC(p). 
    // We have already 'smarktested' the subfaces which directly intersect
    //   with p in 'caveshlist'. From them, we 'smarktest' their neighboring
    //   subfaces which are included in C(p). Do not across a segment.
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      assert(smarktested(*parysh));
      checksh = *parysh;
      for (j = 0; j < 3; j++) {
        if (!isshsubseg(checksh)) {
          spivot(checksh, neighsh);
          assert(neighsh.sh != nullptr);
          if (!smarktested(neighsh)) {
            stpivot(neighsh, neightet);
            if (infected(neightet)) {
              fsymself(neightet);
              if (infected(neightet)) {
                // This subface is inside C(p). 
                // Check if its diametrical circumsphere encloses 'p'.
                //   The purpose of this check is to avoid forming invalid
                //   subcavity in surface mesh.
                sign = incircle3d(sorg(neighsh), sdest(neighsh),
                                  sapex(neighsh), insertpt);
                if (sign < 0) {
                  smarktest(neighsh);
                  caveshlist->newindex((void **) &parysh);
                  *parysh = neighsh;
                }
              }
            }
          }
        }
        senextself(checksh);
      } // j
    } // i
  } // if (ivf->splitbdflag)

  if (ivf->validflag) {
    // Validate C(p) and update it if it is not star-shaped.
    int cutcount = 0;

    if (ivf->respectbdflag) {
      // The initial cavity may include subfaces which are not on the facets
      //   being splitting. Find them and make them as boundary of C(p). 
      // Comment: We have already 'smarktested' the subfaces in sC(p). They
      //   are completely inside C(p). 
      for (i = 0; i < cavetetshlist->objects; i++) {
        parysh = (face *) fastlookup(cavetetshlist, i);
        stpivot(*parysh, neightet);
        if (infected(neightet)) {
          fsymself(neightet);
          if (infected(neightet)) {
            // Found a subface inside C(p).
            if (!smarktested(*parysh)) {
              // It is possible that this face is a boundary subface.
              // Check if it is a hull face.
              //assert(apex(neightet) != dummypoint);
              if (oppo(neightet) != dummypoint) {
                fsymself(neightet);
              }
              if (oppo(neightet) != dummypoint) {
                ori = orient3d(org(neightet), dest(neightet), apex(neightet),
                               insertpt);
                if (ori < 0) {
                  // A visible face, get its neighbor face.
                  fsymself(neightet);
                  ori = -ori; // It must be invisible by p.
                }
              } else {
                // A hull tet. It needs to be cut.
                ori = 1;
              }
              // Cut this tet if it is either invisible by or coplanar with p.
              if (ori >= 0) {
                uninfect(neightet);
                unmarktest(neightet);
                cutcount++;
                neightet.ver = epivot[neightet.ver];
                cavebdrylist->newindex((void **) &parytet);
                *parytet = neightet;
                // Add three new faces to find new boundaries.
                for (j = 0; j < 3; j++) {
                  esym(neightet, neineitet);
                  neineitet.ver = epivot[neineitet.ver];
                  cavebdrylist->newindex((void **) &parytet);
                  *parytet = neineitet;
                  enextself(neightet);
                }
              } // if (ori >= 0) 
            }
          }
        }
      } // i

      // The initial cavity may include segments in its interior. We need to
      //   Update the cavity so that these segments are on the boundary of
      //   the cavity.
      for (i = 0; i < cavetetseglist->objects; i++) {
        paryseg = (face *) fastlookup(cavetetseglist, i);
        // Check this segment if it is not a splitting segment.
        if (!smarktested(*paryseg)) {
          sstpivot1(*paryseg, neightet);
          spintet = neightet;
          while (1) {
            if (!infected(spintet)) break;
            fnextself(spintet);
            if (spintet.tet == neightet.tet) break;
          }
          if (infected(spintet)) {
            // Find an adjacent tet at this segment such that both faces
            //   at this segment are not visible by p.
            pa = org(neightet);
            pb = dest(neightet);
            spintet = neightet;
            j = 0;
            while (1) {
              // Check if this face is visible by p.
              pc = apex(spintet);
              if (pc != dummypoint) {
                ori = orient3d(pa, pb, pc, insertpt);
                if (ori >= 0) {
                  // Not visible. Check another face in this tet.
                  esym(spintet, neineitet);
                  pc = apex(neineitet);
                  if (pc != dummypoint) {
                    ori = orient3d(pb, pa, pc, insertpt);
                    if (ori >= 0) {
                      // Not visible. Found this face.
                      j = 1; // Flag that it is found.
                      break;
                    }
                  }
                }
              }
              fnextself(spintet);
              if (spintet.tet == neightet.tet) break;
            }
            if (j == 0) {
              // Not found such a face.
              assert(0); // debug this case.
            }
            neightet = spintet;
            if (b->verbose > 3) {
               printf("        Cut tet (%d, %d, %d, %d)\n", 
                      pointmark(org(neightet)), pointmark(dest(neightet)),
                      pointmark(apex(neightet)), pointmark(oppo(neightet)));
            }
            uninfect(neightet);
            unmarktest(neightet);
            cutcount++;
            neightet.ver = epivot[neightet.ver];
            cavebdrylist->newindex((void **) &parytet);
            *parytet = neightet;
            // Add three new faces to find new boundaries.
            for (j = 0; j < 3; j++) {
              esym(neightet, neineitet);
              neineitet.ver = epivot[neineitet.ver];
              cavebdrylist->newindex((void **) &parytet);
              *parytet = neineitet;
              enextself(neightet);
            }
          }
        }
      } // i
    } // if (ivf->respectbdflag)

    // Update the cavity by removing invisible faces until it is star-shaped.
    for (i = 0; i < cavebdrylist->objects; i++) {
      cavetet = (triface *) fastlookup(cavebdrylist, i);
      // 'cavetet' is an exterior tet adjacent to the cavity.
      // Check if its neighbor is inside C(p).
      fsym(*cavetet, neightet);
      if (infected(neightet)) {        
        if (apex(*cavetet) != dummypoint) {
          // It is a cavity boundary face. Check its visibility.
          if (oppo(neightet) != dummypoint) {
            ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet),
                           insertpt); 
            enqflag = (ori > 0);
            // Comment: if ori == 0 (coplanar case), we also cut the tet.
          } else {
            // It is a hull face. And its adjacent tet (at inside of the 
            //   domain) has been cut from the cavity. Cut it as well.
            //assert(nonconvex);
            enqflag = false;
          }
        } else {
          enqflag = true; // A hull edge.
        }
        if (enqflag) {
          // This face is valid, save it.
          cavetetlist->newindex((void **) &parytet);
          *parytet = *cavetet; 
        } else {
          uninfect(neightet);
          unmarktest(neightet);
          cutcount++;
          // Add three new faces to find new boundaries.
          for (j = 0; j < 3; j++) {
            esym(neightet, neineitet);
            neineitet.ver = epivot[neineitet.ver];
            cavebdrylist->newindex((void **) &parytet);
            *parytet = neineitet;
            enextself(neightet);
          }
          // 'cavetet' is not on the cavity boundary anymore.
          unmarktest(*cavetet);
        }
      } else {
        // 'cavetet' is not on the cavity boundary anymore.
        unmarktest(*cavetet);
      }
    } // i

    if (cutcount > 0) {
      // The cavity has been updated.
      // Update the cavity boundary faces.
      cavebdrylist->restart();
      for (i = 0; i < cavetetlist->objects; i++) {
        cavetet = (triface *) fastlookup(cavetetlist, i);
        // 'cavetet' was an exterior tet adjacent to the cavity.
        fsym(*cavetet, neightet);
        if (infected(neightet)) {
          // It is a cavity boundary face.
          cavebdrylist->newindex((void **) &parytet);
          *parytet = *cavetet;
        } else {
          // Not a cavity boundary face.
          unmarktest(*cavetet);
        }
      }

      // Update the list of old tets.
      cavetetlist->restart();
      for (i = 0; i < caveoldtetlist->objects; i++) {
        cavetet = (triface *) fastlookup(caveoldtetlist, i);
        if (infected(*cavetet)) {
          cavetetlist->newindex((void **) &parytet);
          *parytet = *cavetet;
        }
      }
      // Swap 'cavetetlist' and 'caveoldtetlist'.
      swaplist = caveoldtetlist;
      caveoldtetlist = cavetetlist;
      cavetetlist = swaplist;

      // The cavity should contain at least one tet.
      if (caveoldtetlist->objects == 0l) {
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) BADELEMENT;
        return 0;
      }

      if (ivf->splitbdflag) { 
        int cutshcount = 0;
        // Update the sub-cavity sC(p).
        for (i = 0; i < caveshlist->objects; i++) {
          parysh = (face *) fastlookup(caveshlist, i);
          if (smarktested(*parysh)) {
            enqflag = false;
            stpivot(*parysh, neightet);
            if (infected(neightet)) {
              fsymself(neightet);
              if (infected(neightet)) {
                enqflag = true;
              }
            }
            if (!enqflag) {
              sunmarktest(*parysh);
              // Use the last entry of this array to fill this entry.
              j = caveshlist->objects - 1;
              checksh = * (face *) fastlookup(caveshlist, j);
              *parysh = checksh;
              cutshcount++;
              caveshlist->objects--; // The list is shrinked.
              i--;
            }
          }
        }

        if (cutshcount > 0) {
          i = 0; // Count the number of invalid subfaces/segments.
          // Valid the updated sub-cavity sC(p).
          if (loc == ONFACE) {
            if ((splitsh != nullptr) && (splitsh->sh != nullptr)) {
              // The to-be split subface should be in sC(p).
              if (!smarktested(*splitsh)) i++;
            }
          } else if (loc == ONEDGE) {
            if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
              // The to-be split segment should be in sC(p).
              if (!smarktested(*splitseg)) i++;
            }
            if ((splitsh != nullptr) && (splitsh->sh != nullptr)) {
              // All subfaces at this edge should be in sC(p).
              pa = sorg(*splitsh);
              neighsh = *splitsh;
              while (1) {
                // Adjust the origin of its edge to be 'pa'.
                if (sorg(neighsh) != pa) {
                  sesymself(neighsh);
                }
                // Add this face into list (in B-W cavity).
                if (!smarktested(neighsh)) i++;
                // Go to the next face at the edge.
                spivotself(neighsh);
                // Stop if all faces at the edge have been visited.
                if (neighsh.sh == splitsh->sh) break;
                if (neighsh.sh == nullptr) break;
              } // while (1)
            }
          }

          if (i > 0) {
            // The updated sC(p) is invalid. Do not insert this vertex.
            insertpoint_abort(splitseg, ivf);
            ivf->iloc = (int) BADELEMENT;
            return 0;
          }
        } // if (cutshcount > 0)
      } // if (ivf->splitbdflag)
    } // if (cutcount > 0)

  } // if (ivf->validflag)

  if (ivf->refineflag) {
    // The new point is inserted by Delaunay refinement, i.e., it is the 
    //   circumcenter of a tetrahedron, or a subface, or a segment.
    //   Do not insert this point if the tetrahedron, or subface, or segment
    //   is not inside the final cavity.
    if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) ||
        ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) {
      insertpoint_abort(splitseg, ivf);
      ivf->iloc = (int) BADELEMENT;
      return 0;
    }
  } // if (ivf->refineflag)

  if (b->plc && (loc != INSTAR)) {
    // Reject the new point if it lies too close to an existing point (b->plc),
    // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4).
    // Collect the list of vertices of the initial cavity.
    if (loc == OUTSIDE) {
      pts = (point *) &(searchtet->tet[4]);
      for (i = 0; i < 3; i++) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = pts[i];
      }
    } else if (loc == INTETRAHEDRON) {
      pts = (point *) &(searchtet->tet[4]);
      for (i = 0; i < 4; i++) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = pts[i];
      } 
    } else if (loc == ONFACE) {
      pts = (point *) &(searchtet->tet[4]);
      for (i = 0; i < 3; i++) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = pts[i];
      }
      if (pts[3] != dummypoint) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = pts[3];
      }
      fsym(*searchtet, spintet);
      if (oppo(spintet) != dummypoint) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = oppo(spintet);
      }
    } else if (loc == ONEDGE) {
      spintet = *searchtet;
      cavetetvertlist->newindex((void **) &parypt);
      *parypt = org(spintet);
      cavetetvertlist->newindex((void **) &parypt);
      *parypt = dest(spintet);
      while (1) {
        if (apex(spintet) != dummypoint) {
          cavetetvertlist->newindex((void **) &parypt);
          *parypt = apex(spintet);
        }
        fnextself(spintet);
        if (spintet.tet == searchtet->tet) break;
      }
    }

    int rejptflag = (ivf->rejflag & 4);
    REAL rd;
    pts = nullptr;

    for (i = 0; i < cavetetvertlist->objects; i++) {
      parypt = (point *) fastlookup(cavetetvertlist, i);
      rd = distance(*parypt, insertpt);
      // Is the point very close to an existing point?
      if (rd < b->minedgelength) {
        pts = parypt; 
        loc = NEARVERTEX;
        break;
      }
      if (rejptflag) {
        // Is the point encroaches upon an existing point?
        if (rd < (*parypt)[pointmtrindex]) {
          pts = parypt;
          loc = ENCVERTEX; 
          break;
        }
      }
    }
    cavetetvertlist->restart(); // Clear the work list.

    if (pts != nullptr) {
      // The point is either too close to an existing vertex (NEARVERTEX)
      //   or encroaches upon (isndie the protecting ball) of that vertex.
      if (loc == NEARVERTEX) {
        if (b->nomergevertex) { // -M0/1 option.
          // In this case, we still insert this vertex. Although it is very
          //   close to an existing vertex. Give a warning, anyway.
          if (!b->quiet) {
            printf("Warning:  Two points, %d and %d, are very close.\n",
                   pointmark(insertpt), pointmark(*pts));
            printf("  Creating a very short edge (len = %g) (< %g).\n",
                   rd, b->minedgelength);
            printf("  You may try a smaller tolerance (-T) (current is %g)\n", 
                   b->epsilon);
            printf("  to avoid this warning.\n");
          }
        } else {
          insertpt[3] = rd; // Only for reporting.
          setpoint2ppt(insertpt, *pts);
          insertpoint_abort(splitseg, ivf);
          ivf->iloc = (int) loc;
          return 0;
        }
      } else { // loc == ENCVERTEX
        // The point lies inside the protection ball.
        setpoint2ppt(insertpt, *pts);
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) loc;
        return 0;
      }
    }
  } // if (b->plc && (loc != INSTAR))

  if (b->weighted || ivf->cdtflag
      ) {
    // There may be other vertices inside C(p). We need to find them.
    // Collect all vertices of C(p).
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      //assert(infected(*cavetet));
      pts = (point *) &(cavetet->tet[4]);
      for (j = 0; j < 4; j++) {
        if (pts[j] != dummypoint) {
          if (!pinfected(pts[j])) {
            pinfect(pts[j]);
            cavetetvertlist->newindex((void **) &parypt);
            *parypt = pts[j];
          }
        }
      } // j
    } // i
    // Uninfect all collected (cavity) vertices.
    for (i = 0; i < cavetetvertlist->objects; i++) {
      parypt = (point *) fastlookup(cavetetvertlist, i);
      puninfect(*parypt);
    }
  }


  if (ivf->cdtflag) {
    // Unmark tets.
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      unmarktest(*cavetet);
    }
    for (i = 0; i < cavebdrylist->objects; i++) {
      cavetet = (triface *) fastlookup(cavebdrylist, i);
      unmarktest(*cavetet);
    }
    // Clean up arrays which are not needed.
    cavetetlist->restart();
    if (checksubsegflag) {
      cavetetseglist->restart();
    }
    if (checksubfaceflag) {
      cavetetshlist->restart();
    }
    return 1;
  }

  // Before re-mesh C(p). Process the segments and subfaces which are on the
  //   boundary of C(p). Make sure that each such segment or subface is
  //   connecting to a tet outside C(p). So we can re-connect them to the
  //   new tets inside the C(p) later.

  if (checksubsegflag) {
    for (i = 0; i < cavetetseglist->objects; i++) {
      paryseg = (face *) fastlookup(cavetetseglist, i);
      // Operate on it if it is not the splitting segment, i.e., in sC(p).
      if (!smarktested(*paryseg)) {
        // Check if the segment is inside the cavity.
        //   'j' counts the num of adjacent tets of this seg.
        //   'k' counts the num of adjacent tets which are 'sinfected'.
        j = k = 0;
        sstpivot1(*paryseg, neightet);
        spintet = neightet;
        while (1) {
          j++;
          if (!infected(spintet)) {
            neineitet = spintet; // An outer tet. Remember it.
          } else {
            k++; // An in tet.
          }
          fnextself(spintet);
          if (spintet.tet == neightet.tet) break;
        }
        // assert(j > 0);
        if (k == 0) {
          // The segment is not connect to C(p) anymore. Remove it by
          //   Replacing it by the last entry of this list.
          s = cavetetseglist->objects - 1;
          checkseg = * (face *) fastlookup(cavetetseglist, s);
          *paryseg = checkseg;
          cavetetseglist->objects--;
          i--;
        } else if (k < j) {
          // The segment is on the boundary of C(p).
          sstbond1(*paryseg, neineitet);
        } else { // k == j
          // The segment is inside C(p).
          if (!ivf->splitbdflag) {
            checkseg = *paryseg;
            sinfect(checkseg); // Flag it as an interior segment.
            caveencseglist->newindex((void **) &paryseg);
            *paryseg = checkseg;
          } else {
            assert(0); // Not possible.
          }
        }
      } else { 
        // assert(smarktested(*paryseg));
        // Flag it as an interior segment. Do not queue it, since it will
        //   be deleted after the segment splitting.
        sinfect(*paryseg);
      }
    } // i
  } // if (checksubsegflag)

  if (checksubfaceflag) {
    for (i = 0; i < cavetetshlist->objects; i++) {
      parysh = (face *) fastlookup(cavetetshlist, i);
      // Operate on it if it is not inside the sub-cavity sC(p).
      if (!smarktested(*parysh)) {
        // Check if this subface is inside the cavity.
        k = 0;
        for (j = 0; j < 2; j++) {
          stpivot(*parysh, neightet);
          if (!infected(neightet)) {
            checksh = *parysh; // Remeber this side.
          } else {
            k++;
          }
          sesymself(*parysh);
        }
        if (k == 0) {
          // The subface is not connected to C(p). Remove it.
          s = cavetetshlist->objects - 1;
          checksh = * (face *) fastlookup(cavetetshlist, s);
          *parysh = checksh;
          cavetetshlist->objects--;
          i--;
        } else if (k == 1) {
          // This side is the outer boundary of C(p).
          *parysh = checksh;
        } else { // k == 2
          if (!ivf->splitbdflag) {
            checksh = *parysh;
            sinfect(checksh); // Flag it.
            caveencshlist->newindex((void **) &parysh);
            *parysh = checksh;
          } else {
            assert(0); // Not possible.
          }
        }
      } else {
        // assert(smarktested(*parysh));
        // Flag it as an interior subface. Do not queue it. It will be
        //   deleted after the facet point insertion.
        sinfect(*parysh);
      }
    } // i
  } // if (checksubfaceflag)

  // Create new tetrahedra to fill the cavity.

  for (i = 0; i < cavebdrylist->objects; i++) {
    cavetet = (triface *) fastlookup(cavebdrylist, i);
    neightet = *cavetet;
    unmarktest(neightet); // Unmark it.
    // Get the oldtet (inside the cavity).
    fsym(neightet, oldtet);
    if (apex(neightet) != dummypoint) {
      // Create a new tet in the cavity.
      maketetrahedron(&newtet);
      setorg(newtet, dest(neightet));
      setdest(newtet, org(neightet));
      setapex(newtet, apex(neightet));
      setoppo(newtet, insertpt);
    } else {
      // Create a new hull tet.
      hullsize++; 
      maketetrahedron(&newtet);
      setorg(newtet, org(neightet));
      setdest(newtet, dest(neightet));
      setapex(newtet, insertpt);
      setoppo(newtet, dummypoint); // It must opposite to face 3.
      // Adjust back to the cavity bounday face.
      esymself(newtet);
    }
    // The new tet inherits attribtes from the old tet.
    for (j = 0; j < numelemattrib; j++) {
      attrib = elemattribute(oldtet.tet, j);
      setelemattribute(newtet.tet, j, attrib);
    }
    if (b->varvolume) {
      volume = volumebound(oldtet.tet);
      setvolumebound(newtet.tet, volume);
    }
    // Connect newtet <==> neightet, this also disconnect the old bond.
    bond(newtet, neightet);
    // oldtet still connects to neightet.
    *cavetet = oldtet; // *cavetet = newtet;
  } // i

  // Set a handle for speeding point location.
  recenttet = newtet;
  //setpoint2tet(insertpt, encode(newtet));
  setpoint2tet(insertpt, (tetrahedron) (newtet.tet));

  // Re-use this list to save new interior cavity faces.
  cavetetlist->restart();

  // Connect adjacent new tetrahedra together.
  for (i = 0; i < cavebdrylist->objects; i++) {
    cavetet = (triface *) fastlookup(cavebdrylist, i);
    // cavtet is an oldtet, get the newtet at this face.
    oldtet = *cavetet;
    fsym(oldtet, neightet);
    fsym(neightet, newtet);
    // Comment: oldtet and newtet must be at the same directed edge.
    // Connect the three other faces of this newtet.
    for (j = 0; j < 3; j++) {
      esym(newtet, neightet); // Go to the face.
      if (neightet.tet[neightet.ver & 3] == nullptr) {
        // Find the adjacent face of this newtet.
        spintet = oldtet;
        while (1) {
          fnextself(spintet);
          if (!infected(spintet)) break;
        }
        fsym(spintet, newneitet);
        esymself(newneitet);
        assert(newneitet.tet[newneitet.ver & 3] == nullptr);
        bond(neightet, newneitet);
        if (ivf->lawson > 1) { 
          cavetetlist->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
      //setpoint2tet(org(newtet), encode(newtet));
      setpoint2tet(org(newtet), (tetrahedron) (newtet.tet));
      enextself(newtet);
      enextself(oldtet);
    }
    *cavetet = newtet; // Save the new tet.
  } // i

  if (checksubfaceflag) {
    // Connect subfaces on the boundary of the cavity to the new tets.
    for (i = 0; i < cavetetshlist->objects; i++) {
      parysh = (face *) fastlookup(cavetetshlist, i);
      // Connect it if it is not a missing subface.
      if (!sinfected(*parysh)) {
        stpivot(*parysh, neightet);
        fsym(neightet, spintet);
        sesymself(*parysh);
        tsbond(spintet, *parysh);
      }
    }
  }

  if (checksubsegflag) {
    // Connect segments on the boundary of the cavity to the new tets.
    for (i = 0; i < cavetetseglist->objects; i++) {
      paryseg = (face *) fastlookup(cavetetseglist, i);
      // Connect it if it is not a missing segment.
      if (!sinfected(*paryseg)) {
        sstpivot1(*paryseg, neightet);
        spintet = neightet;
        while (1) {
          tssbond1(spintet, *paryseg);
          fnextself(spintet);
          if (spintet.tet == neightet.tet) break;
        }
      }
    }
  }

  if (((splitsh != nullptr) && (splitsh->sh != nullptr)) ||
      ((splitseg != nullptr) && (splitseg->sh != nullptr))) {
    // Split a subface or a segment.
    sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0);
  }

  if (checksubfaceflag) {
    if (ivf->splitbdflag) {
      // Recover new subfaces in C(p).
      for (i = 0; i < caveshbdlist->objects; i++) {
        // Get an old subface at edge [a, b].
        parysh = (face *) fastlookup(caveshbdlist, i);
        spivot(*parysh, checksh); // The new subface [a, b, p].
        // Do not recover a deleted new face (degenerated).
        if (checksh.sh[3] != nullptr) {
          // Note that the old subface still connects to adjacent old tets 
          //   of C(p), which still connect to the tets outside C(p).
          stpivot(*parysh, neightet);
          assert(infected(neightet));
          // Find the adjacent tet containing the edge [a,b] outside C(p).
          spintet = neightet;
          while (1) {
            fnextself(spintet);
            if (!infected(spintet)) break;
            assert(spintet.tet != neightet.tet);
          }
          // The adjacent tet connects to a new tet in C(p).
          fsym(spintet, neightet);
          assert(!infected(neightet));
          // Find the tet containing the face [a, b, p].
          spintet = neightet;
          while (1) {
            fnextself(spintet);
            if (apex(spintet) == insertpt) break;
            assert(spintet.tet != neightet.tet);
          }
          // Adjust the edge direction in spintet and checksh.
          if (sorg(checksh) != org(spintet)) {
            sesymself(checksh);
            assert(sorg(checksh) == org(spintet));
          }
          assert(sdest(checksh) == dest(spintet));
          // Connect the subface to two adjacent tets.
          tsbond(spintet, checksh);
          fsymself(spintet);
          sesymself(checksh);
          tsbond(spintet, checksh);
        } // if (checksh.sh[3] != nullptr)
      }
      // There should be no missing interior subfaces in C(p).
      assert(caveencshlist->objects == 0l);
    } else { 
      // The Boundary reocvery phase.
      // Put all new subfaces into stack for recovery.
      for (i = 0; i < caveshbdlist->objects; i++) {
        // Get an old subface at edge [a, b].
        parysh = (face *) fastlookup(caveshbdlist, i);
        spivot(*parysh, checksh); // The new subface [a, b, p].
        // Do not recover a deleted new face (degenerated).
        if (checksh.sh[3] != nullptr) {
          subfacstack->newindex((void **) &parysh);
          *parysh = checksh;
        }
      }
      // Put all interior subfaces into stack for recovery.
      for (i = 0; i < caveencshlist->objects; i++) {
        parysh = (face *) fastlookup(caveencshlist, i);
        assert(sinfected(*parysh));
        // Some subfaces inside C(p) might be split in sinsertvertex().
        //   Only queue those faces which are not split.
        if (!smarktested(*parysh)) {
          checksh = *parysh;
          suninfect(checksh);
          stdissolve(checksh); // Detach connections to old tets.
          subfacstack->newindex((void **) &parysh);
          *parysh = checksh;
        }
      }
    }
  } // if (checksubfaceflag)

  if (checksubsegflag) {
    if (ivf->splitbdflag) {
      if (splitseg != nullptr) {
        // Recover the two new subsegments in C(p).
        for (i = 0; i < cavesegshlist->objects; i++) {
          paryseg = (face *) fastlookup(cavesegshlist, i);
          // Insert this subsegment into C(p).
          checkseg = *paryseg;
          // Get the adjacent new subface.
          checkseg.shver = 0;
          spivot(checkseg, checksh);
          if (checksh.sh != nullptr) {
            // Get the adjacent new tetrahedron.
            stpivot(checksh, neightet);
          } else {
            // It's a dangling segment.
            point2tetorg(sorg(checkseg), neightet);
            finddirection(&neightet, sdest(checkseg));
            assert(dest(neightet) == sdest(checkseg));
          }
          assert(!infected(neightet));
          sstbond1(checkseg, neightet);
          spintet = neightet;
          while (1) {
            tssbond1(spintet, checkseg);
            fnextself(spintet);
            if (spintet.tet == neightet.tet) break;
          }
        }
      } // if (splitseg != nullptr)
      // There should be no interior segment in C(p).
      assert(caveencseglist->objects == 0l);
    } else {
      // The Boundary Recovery Phase.  
      // Queue missing segments in C(p) for recovery.
      if (splitseg != nullptr) {
        // Queue two new subsegments in C(p) for recovery.
        for (i = 0; i < cavesegshlist->objects; i++) {
          paryseg = (face *) fastlookup(cavesegshlist, i);
          checkseg = *paryseg;
          //sstdissolve1(checkseg); // It has not been connected yet.
          s = randomnation(subsegstack->objects + 1);
          subsegstack->newindex((void **) &paryseg);
          *paryseg = * (face *) fastlookup(subsegstack, s); 
          paryseg = (face *) fastlookup(subsegstack, s);
          *paryseg = checkseg;
        }
      } // if (splitseg != nullptr)
      for (i = 0; i < caveencseglist->objects; i++) {
        paryseg = (face *) fastlookup(caveencseglist, i);
        assert(sinfected(*paryseg));
        if (!smarktested(*paryseg)) { // It may be split.
          checkseg = *paryseg;
          suninfect(checkseg);
          sstdissolve1(checkseg); // Detach connections to old tets.
          s = randomnation(subsegstack->objects + 1);
          subsegstack->newindex((void **) &paryseg);
          *paryseg = * (face *) fastlookup(subsegstack, s); 
          paryseg = (face *) fastlookup(subsegstack, s);
          *paryseg = checkseg;
        }
      }
    }
  } // if (checksubsegflag)

  if (b->weighted
      ) {
    // Some vertices may be completed inside the cavity. They must be
    //   detected and added to recovering list.
    // Since every "live" vertex must contain a pointer to a non-dead
    //   tetrahedron, we can check for each vertex this pointer.
    for (i = 0; i < cavetetvertlist->objects; i++) {
      pts = (point *) fastlookup(cavetetvertlist, i);
      decode(point2tet(*pts), *searchtet);
      assert(searchtet->tet != nullptr); // No tet has been deleted yet.
      if (infected(*searchtet)) {
        if (b->weighted) {
          if (b->verbose > 1) {
            printf("    Point #%d is non-regular after the insertion of #%d.\n",
                   pointmark(*pts), pointmark(insertpt));
          }
          setpointtype(*pts, NREGULARVERTEX);
          nonregularcount++;
        }
      }
    }
  }

  if (ivf->chkencflag & 1) {
    // Queue all segment outside C(p).
    for (i = 0; i < cavetetseglist->objects; i++) {
      paryseg = (face *) fastlookup(cavetetseglist, i);
      // Skip if it is the split segment.
      if (!sinfected(*paryseg)) {
        enqueuesubface(badsubsegs, paryseg);
      }
    }
    if (splitseg != nullptr) {
      // Queue the two new subsegments inside C(p).
      for (i = 0; i < cavesegshlist->objects; i++) {
        paryseg = (face *) fastlookup(cavesegshlist, i);
        enqueuesubface(badsubsegs, paryseg);
      }
    }
  } // if (chkencflag & 1)

  if (ivf->chkencflag & 2) {
    // Queue all subfaces outside C(p).
    for (i = 0; i < cavetetshlist->objects; i++) {
      parysh = (face *) fastlookup(cavetetshlist, i);
      // Skip if it is a split subface.
      if (!sinfected(*parysh)) {
        enqueuesubface(badsubfacs, parysh);
      }
    }
    // Queue all new subfaces inside C(p).
    for (i = 0; i < caveshbdlist->objects; i++) {
      // Get an old subface at edge [a, b].
      parysh = (face *) fastlookup(caveshbdlist, i);
      spivot(*parysh, checksh); // checksh is a new subface [a, b, p].
      // Do not recover a deleted new face (degenerated).
      if (checksh.sh[3] != nullptr) {
        enqueuesubface(badsubfacs, &checksh);
      }
    }
  } // if (chkencflag & 2)

  if (ivf->chkencflag & 4) {
    // Queue all new tetrahedra in C(p).
    for (i = 0; i < cavebdrylist->objects; i++) {
      cavetet = (triface *) fastlookup(cavebdrylist, i);
      enqueuetetrahedron(cavetet);
    }
  }

  // C(p) is re-meshed successfully.

  // Delete the old tets in C(p).
  for (i = 0; i < caveoldtetlist->objects; i++) {
    searchtet = (triface *) fastlookup(caveoldtetlist, i);
    if (ishulltet(*searchtet)) {
      hullsize--;
    }
    tetrahedrondealloc(searchtet->tet);
  }

  if (((splitsh != nullptr) && (splitsh->sh != nullptr)) ||
      ((splitseg != nullptr) && (splitseg->sh != nullptr))) {
    // Delete the old subfaces in sC(p).
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      if (checksubfaceflag) {//if (bowywat == 2) {
        // It is possible that this subface still connects to adjacent
        //   tets which are not in C(p). If so, clear connections in the
        //   adjacent tets at this subface.
        stpivot(*parysh, neightet);
        if (neightet.tet != nullptr) {
          if (neightet.tet[4] != nullptr) {
            // Found an adjacent tet. It must be not in C(p).
            assert(!infected(neightet));
            tsdissolve(neightet);
            fsymself(neightet);
            assert(!infected(neightet));
            tsdissolve(neightet);
          }
        }
      }
      shellfacedealloc(subfaces, parysh->sh);
    }
    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      // Delete the old segment in sC(p).
      shellfacedealloc(subsegs, splitseg->sh);
    }
  }

  if (ivf->lawson) {
    for (i = 0; i < cavebdrylist->objects; i++) {
      searchtet = (triface *) fastlookup(cavebdrylist, i);
      flippush(flipstack, searchtet);
    }
    if (ivf->lawson > 1) {
      for (i = 0; i < cavetetlist->objects; i++) {
        searchtet = (triface *) fastlookup(cavetetlist, i);
        flippush(flipstack, searchtet);
      }
    }
  }

  // Set the point type if it is not set yet.
  if (pointtype(insertpt) == UNUSEDVERTEX) {
    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      setpointtype(insertpt, FREESEGVERTEX);
    } else if ((splitsh != nullptr) && (splitsh->sh != nullptr)) {
      setpointtype(insertpt, FREEFACETVERTEX);
    } else {
      setpointtype(insertpt, FREEVOLVERTEX);
    }
  }

  // Clean the working lists.

  caveoldtetlist->restart();
  cavebdrylist->restart();
  cavetetlist->restart();

  if (checksubsegflag) {
    cavetetseglist->restart();
    caveencseglist->restart();
  }

  if (checksubfaceflag) {
    cavetetshlist->restart();
    caveencshlist->restart();
  }
  
  if (b->weighted || ivf->validflag) {
    cavetetvertlist->restart();
  }
  
  if (((splitsh != nullptr) && (splitsh->sh != nullptr)) ||
      ((splitseg != nullptr) && (splitseg->sh != nullptr))) {
    caveshlist->restart();
    caveshbdlist->restart();
    cavesegshlist->restart();
  }

  return 1; // Point is inserted.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertpoint_abort()    Abort the insertion of a new vertex.               //
//                                                                           //
// The cavity will be restored.  All working lists are cleared.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf)
{
  triface *cavetet;
  face *parysh;
  int i;

  for (i = 0; i < caveoldtetlist->objects; i++) {
    cavetet = (triface *) fastlookup(caveoldtetlist, i);
    uninfect(*cavetet);
    unmarktest(*cavetet);
  }
  for (i = 0; i < cavebdrylist->objects; i++) {
    cavetet = (triface *) fastlookup(cavebdrylist, i);
    unmarktest(*cavetet); 
  }
  cavetetlist->restart();
  cavebdrylist->restart();
  caveoldtetlist->restart();
  cavetetseglist->restart();
  cavetetshlist->restart();
  if (ivf->splitbdflag) { 
    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      sunmarktest(*splitseg);
    }
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      assert(smarktested(*parysh));
      sunmarktest(*parysh);
    }
    caveshlist->restart();
    cavesegshlist->restart();
  }
}

////                                                                       ////
////                                                                       ////
//// flip_cxx /////////////////////////////////////////////////////////////////

//// delaunay_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// transfernodes()    Read the vertices from the input (tetgenio).           //
//                                                                           //
// Transferring all points from input ('in->pointlist') to TetGen's 'points'.//
// All points are indexed (the first point index is 'in->firstnumber'). Each //
// point's type is initialized as UNUSEDVERTEX. The bounding box (xmax, xmin,//
// ...) and the diameter (longest) of the point set are calculated.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::transfernodes()
{
  point pointloop;
  REAL x, y, z, w;
  int coordindex;
  int attribindex;
  int mtrindex;
  int i, j;

  if (b->psc) {
    assert(in->pointparamlist != nullptr);
  }

  // Read the points.
  coordindex = 0;
  attribindex = 0;
  mtrindex = 0;
  for (i = 0; i < in->numberofpoints; i++) {
    makepoint(&pointloop, UNUSEDVERTEX);
    // Read the point coordinates.
    x = pointloop[0] = in->pointlist[coordindex++];
    y = pointloop[1] = in->pointlist[coordindex++];
    z = pointloop[2] = in->pointlist[coordindex++];
    // Read the point attributes. (Including point weights.)
    for (j = 0; j < in->numberofpointattributes; j++) {
      pointloop[3 + j] = in->pointattributelist[attribindex++];
    }
    // Read the point metric tensor.
    for (j = 0; j < in->numberofpointmtrs; j++) {
      pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
    }
    if (b->weighted) { // -w option
      if (in->numberofpointattributes > 0) {
        // The first point attribute is its weight.
        //w = in->pointattributelist[in->numberofpointattributes * i];
        w = pointloop[3];
      } else {
        // No given weight available. Default choose the maximum
        //   absolute value among its coordinates.        
        w = fabs(x);
        if (w < fabs(y)) w = fabs(y);
        if (w < fabs(z)) w = fabs(z);
      }
      if (b->weighted_param == 0) {
        pointloop[3] = x * x + y * y + z * z - w; // Weighted DT.
      } else { // -w1 option
        pointloop[3] = w;  // Regular tetrahedralization.
      }
    }
    // Determine the smallest and largests x, y and z coordinates.
    if (i == 0) {
      xmin = xmax = x;
      ymin = ymax = y;
      zmin = zmax = z;
    } else {
      xmin = (x < xmin) ? x : xmin;
      xmax = (x > xmax) ? x : xmax;
      ymin = (y < ymin) ? y : ymin;
      ymax = (y > ymax) ? y : ymax;
      zmin = (z < zmin) ? z : zmin;
      zmax = (z > zmax) ? z : zmax;
    }
    if (b->psc) {
      // Read the geometry parameters.
      setpointgeomuv(pointloop, 0, in->pointparamlist[i].uv[0]);
      setpointgeomuv(pointloop, 1, in->pointparamlist[i].uv[1]);
      setpointgeomtag(pointloop, in->pointparamlist[i].tag);
      if (in->pointparamlist[i].type == 0) {
        setpointtype(pointloop, RIDGEVERTEX);
      } else if (in->pointparamlist[i].type == 1) {
        setpointtype(pointloop, FREESEGVERTEX);
      } else if (in->pointparamlist[i].type == 2) {
        setpointtype(pointloop, FREEFACETVERTEX);
      } else if (in->pointparamlist[i].type == 3) {
        setpointtype(pointloop, FREEVOLVERTEX);
      }
    }
  }

  // 'longest' is the largest possible edge length formed by input vertices.
  x = xmax - xmin;
  y = ymax - ymin;
  z = zmax - zmin;
  longest = sqrt(x * x + y * y + z * z);
  if (longest == 0.0) {
    printf("Error:  The point set is trivial.\n");
    terminatetetgen(3);
  }

  // Two identical points are distinguished by 'lengthlimit'.
  if (b->minedgelength == 0.0) {
    b->minedgelength = longest * b->epsilon;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hilbert_init()    Initialize the Gray code permutation table.             //
//                                                                           //
// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray  //
// code sequences traveled by the 1st order Hilbert curve in 3 dimensions.   //
// The first column is the Gray code of the entry point of the curve, and    //
// the second column is the direction (0, 1, or 2, 0 means the x-axis) where //
// the exit point of curve lies.                                             //
//                                                                           //
// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the //
// indices from 0 to 7, modulo by '3'. The code for generating this table is //
// from: http://graphics.stanford.edu/~seander/bithacks.html.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::hilbert_init(int n)
{
  int gc[8], N, mask, travel_bit;
  int e, d, f, k, g;
  int v, c;
  int i;

  N = (n == 2) ? 4 : 8;
  mask = (n == 2) ? 3 : 7;

  // Generate the Gray code sequence.
  for (i = 0; i < N; i++) {
    gc[i] = i ^ (i >> 1);
  }

  for (e = 0; e < N; e++) {
    for (d = 0; d < n; d++) {
      // Calculate the end point (f).
      f = e ^ (1 << d);  // Toggle the d-th bit of 'e'.
      // travel_bit = 2**p, the bit we want to travel. 
      travel_bit = e ^ f;
      for (i = 0; i < N; i++) {
        // // Rotate gc[i] left by (p + 1) % n bits.
        k = gc[i] * (travel_bit * 2);
        g = ((k | (k / N)) & mask);
        // Calculate the permuted Gray code by xor with the start point (e).
        transgc[e][d][i] = (g ^ e);
      }
      assert(transgc[e][d][0] == e);
      assert(transgc[e][d][N - 1] == f);
    } // d
  } // e

  // Count the consecutive '1' bits (trailing) on the right.
  tsb1mod3[0] = 0;
  for (i = 1; i < N; i++) {
    v = ~i; // Count the 0s.
    v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest
    for (c = 0; v; c++) {
      v >>= 1;
    }
    tsb1mod3[i] = c % n;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hilbert_sort3()    Sort points using the 3d Hilbert curve.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1,
                              REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, 
                              REAL bzmin, REAL bzmax)
{
  point swapvert;
  int axis, d;
  REAL split;
  int i, j;


  // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which 
  //   correspoding to x-, or y- or z-axis.
  axis = (gc0 ^ gc1) >> 1; 

  // Calulate the split position along the axis.
  if (axis == 0) {
    split = 0.5 * (bxmin + bxmax);
  } else if (axis == 1) {
    split = 0.5 * (bymin + bymax);
  } else { // == 2
    split = 0.5 * (bzmin + bzmax);
  }

  // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction
  //   of the axis is to the positive of the axis, otherwise, it is -1.
  d = ((gc0 & (1<<axis)) == 0) ? 1 : -1;


  // Partition the vertices into left- and right-arraies such that left points
  //   have Hilbert indices lower than the right points.
  i = 0;
  j = arraysize - 1;

  // Partition the vertices into left- and right-arraies.
  if (d > 0) {
    do {
      for (; i < arraysize; i++) {      
        if (vertexarray[i][axis] >= split) break;
      }
      for (; j >= 0; j--) {
        if (vertexarray[j][axis] < split) break;
      }
      // Is the partition finished?
      if (i == (j + 1)) break;
      // Swap i-th and j-th vertices.
      swapvert = vertexarray[i];
      vertexarray[i] = vertexarray[j];
      vertexarray[j] = swapvert;
      // Continue patitioning the array;
    } while (true);
  } else {
    do {
      for (; i < arraysize; i++) {      
        if (vertexarray[i][axis] <= split) break;
      }
      for (; j >= 0; j--) {
        if (vertexarray[j][axis] > split) break;
      }
      // Is the partition finished?
      if (i == (j + 1)) break;
      // Swap i-th and j-th vertices.
      swapvert = vertexarray[i];
      vertexarray[i] = vertexarray[j];
      vertexarray[j] = swapvert;
      // Continue patitioning the array;
    } while (true);
  }

  return i;
}

void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, 
                               REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, 
                               REAL bzmin, REAL bzmax, int depth)
{
  REAL x1, x2, y1, y2, z1, z2;
  int p[9], w, e_w, d_w, k, ei, di;
  int n = 3, mask = 7;

  p[0] = 0;
  p[8] = arraysize;

  // Sort the points according to the 1st order Hilbert curve in 3d.
  p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
  p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
  p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax);
  p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2], 
                       transgc[e][d][2], transgc[e][d][3], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2];
  p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4], 
                       transgc[e][d][5], transgc[e][d][6], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
  p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4], 
                       transgc[e][d][4], transgc[e][d][5], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
  p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6], 
                       transgc[e][d][6], transgc[e][d][7], 
                       bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6];

  if (b->hilbert_order > 0) {
    // A maximum order is prescribed. 
    if ((depth + 1) == b->hilbert_order) {
      // The maximum prescribed order is reached.
      return;
    }
  }

  // Recursivly sort the points in sub-boxes.
  for (w = 0; w < 8; w++) {
    // w is the local Hilbert index (NOT Gray code).
    // Sort into the sub-box either there are more than 2 points in it, or
    //   the prescribed order of the curve is not reached yet.
    //if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) {
    if ((p[w+1] - p[w]) > b->hilbert_limit) {
      // Calulcate the start point (ei) of the curve in this sub-box.
      //   update e = e ^ (e(w) left_rotate (d+1)).
      if (w == 0) {
        e_w = 0;
      } else {
        //   calculate e(w) = gc(2 * floor((w - 1) / 2)).
        k = 2 * ((w - 1) / 2); 
        e_w = k ^ (k >> 1); // = gc(k).
      }
      k = e_w;
      e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask);
      ei = e ^ e_w;
      // Calulcate the direction (di) of the curve in this sub-box.
      //   update d = (d + d(w) + 1) % n
      if (w == 0) {
        d_w = 0;
      } else {
        d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w];
      }
      di = (d + d_w + 1) % n;
      // Calculate the bounding box of the sub-box.
      if (transgc[e][d][w] & 1) { // x-axis
        x1 = 0.5 * (bxmin + bxmax);
        x2 = bxmax;
      } else {
        x1 = bxmin;
        x2 = 0.5 * (bxmin + bxmax);
      }
      if (transgc[e][d][w] & 2) { // y-axis
        y1 = 0.5 * (bymin + bymax);
        y2 = bymax;
      } else {
        y1 = bymin;
        y2 = 0.5 * (bymin + bymax);
      }
      if (transgc[e][d][w] & 4) { // z-axis
        z1 = 0.5 * (bzmin + bzmax);
        z2 = bzmax;
      } else {
        z1 = bzmin;
        z2 = 0.5 * (bzmin + bzmax);
      }
      hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di, 
                    x1, x2, y1, y2, z1, z2, depth+1);
    } // if (p[w+1] - p[w] > 1)
  } // w
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// brio_multiscale_sort()    Sort the points using BRIO and Hilbert curve.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, 
                                      int threshold, REAL ratio, int *depth)
{
  int middle;

  middle = 0;
  if (arraysize >= threshold) {
    (*depth)++;
    middle = (int)(arraysize * ratio);
    brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth);
  }
  // Sort the right-array (rnd-th round) using the Hilbert curve.
  hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d
                xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// randomnation()    Generate a random number between 0 and 'choices' - 1.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

unsigned long tetgenmesh::randomnation(unsigned int choices)
{
  unsigned long newrandom;

  if (choices >= 714025l) {
    newrandom = (randomseed * 1366l + 150889l) % 714025l;
    randomseed = (newrandom * 1366l + 150889l) % 714025l;
    newrandom = newrandom * (choices / 714025l) + randomseed;
    if (newrandom >= choices) {
      return newrandom - choices;
    } else {
      return newrandom;
    }
  } else {
    randomseed = (randomseed * 1366l + 150889l) % 714025l;
    return randomseed % choices;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// randomsample()    Randomly sample the tetrahedra for point loation.       //
//                                                                           //
// Searching begins from one of handles:  the input 'searchtet', a recently  //
// encountered tetrahedron 'recenttet',  or from one chosen from a random    //
// sample.  The choice is made by determining which one's origin is closest  //
// to the point we are searcing for.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::randomsample(point searchpt,triface *searchtet)
{
  tetrahedron *firsttet, *tetptr;
  point torg;
  void **sampleblock;
  uintptr_t alignptr;
  long sampleblocks, samplesperblock, samplenum;
  long tetblocks, i, j;
  REAL searchdist, dist;

  if (b->verbose > 2) {
    printf("      Random sampling tetrahedra for searching point %d.\n",
           pointmark(searchpt));
  }

  if (!nonconvex) {
    if (searchtet->tet == nullptr) {
      // A null tet. Choose the recenttet as the starting tet.
      *searchtet = recenttet;
      // Recenttet should not be dead.
      assert(recenttet.tet[4] != nullptr);
    }

    // 'searchtet' should be a valid tetrahedron. Choose the base face
    //   whose vertices must not be 'dummypoint'.
    searchtet->ver = 3;
    // Record the distance from its origin to the searching point.
    torg = org(*searchtet);
    searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
                 (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
                 (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);

    // If a recently encountered tetrahedron has been recorded and has not
    //   been deallocated, test it as a good starting point.
    if (recenttet.tet != searchtet->tet) {
      recenttet.ver = 3;
      torg = org(recenttet);
      dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
             (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
             (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
      if (dist < searchdist) {
        *searchtet = recenttet;
        searchdist = dist;
      }
    }
  } else {
    // The mesh is non-convex. Do not use 'recenttet'.
    assert(samples >= 1l); // Make sure at least 1 sample.
    searchdist = longest;
  }

  // Select "good" candidate using k random samples, taking the closest one.
  //   The number of random samples taken is proportional to the fourth root
  //   of the number of tetrahedra in the mesh. 
  while (samples * samples * samples * samples < tetrahedrons->items) {
    samples++;
  }
  // Find how much blocks in current tet pool.
  tetblocks = (tetrahedrons->maxitems + b->tetrahedraperblock - 1) 
            / b->tetrahedraperblock;
  // Find the average samples per block. Each block at least have 1 sample.
  samplesperblock = 1 + (samples / tetblocks);
  sampleblocks = samples / samplesperblock;
  sampleblock = tetrahedrons->firstblock;
  for (i = 0; i < sampleblocks; i++) {
    alignptr = (uintptr_t) (sampleblock + 1);
    firsttet = (tetrahedron *)
               (alignptr + (uintptr_t) tetrahedrons->alignbytes
               - (alignptr % (uintptr_t) tetrahedrons->alignbytes));
    for (j = 0; j < samplesperblock; j++) {
      if (i == tetblocks - 1) {
        // This is the last block.
        samplenum = randomnation((int)
                      (tetrahedrons->maxitems - (i * b->tetrahedraperblock)));
      } else {
        samplenum = randomnation(b->tetrahedraperblock);
      }
      tetptr = (tetrahedron *)
               (firsttet + (samplenum * tetrahedrons->itemwords));
      torg = (point) tetptr[4];
      if (torg != (point) nullptr) {
        dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
               (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
               (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
        if (dist < searchdist) {
          searchtet->tet = tetptr;
          searchtet->ver = 11; // torg = org(t);
          searchdist = dist;
        }
      } else {
        // A dead tet. Re-sample it.
        if (i != tetblocks - 1) j--;
      }
    }
    sampleblock = (void **) *sampleblock;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locate()    Find a tetrahedron containing a given point.                  //
//                                                                           //
// Begins its search from 'searchtet', assume there is a line segment L from //
// a vertex of 'searchtet' to the query point 'searchpt', and simply walk    //
// towards 'searchpt' by traversing all faces intersected by L.              //
//                                                                           //
// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The //
// returned value indicates one of the following cases:                      //
//   - ONVERTEX, the search point lies on the origin of 'searchtet'.         //
//   - ONEDGE, the search point lies on an edge of 'searchtet'.              //
//   - ONFACE, the search point lies on a face of 'searchtet'.               //
//   - INTET, the search point lies in the interior of 'searchtet'.          //
//   - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a     //
//              hull face which is visible by the search point.              //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult 
  tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag)
{
  point torg, tdest, tapex, toppo;
  enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
  REAL ori, oriorg, oridest, oriapex;
  enum locateresult loc = OUTSIDE;
  int t1ver;
  int s;

  if (searchtet->tet == nullptr) {
    // A null tet. Choose the recenttet as the starting tet.
    searchtet->tet = recenttet.tet;
  }

  // Check if we are in the outside of the convex hull.
  if (ishulltet(*searchtet)) {
    // Get its adjacent tet (inside the hull).
    searchtet->ver = 3;
    fsymself(*searchtet);
  }

  // Let searchtet be the face such that 'searchpt' lies above to it.
  for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) {
    torg = org(*searchtet);
    tdest = dest(*searchtet);
    tapex = apex(*searchtet);
    ori = orient3d(torg, tdest, tapex, searchpt); 
    if (ori < 0.0) break;
  }
  assert(searchtet->ver != 4);

  // Walk through tetrahedra to locate the point.
  while (true) {

    toppo = oppo(*searchtet);
    
    // Check if the vertex is we seek.
    if (toppo == searchpt) {
      // Adjust the origin of searchtet to be searchpt.
      esymself(*searchtet);
      eprevself(*searchtet);
      loc = ONVERTEX; // return ONVERTEX;
      break;
    }

    // We enter from one of serarchtet's faces, which face do we exit?
    oriorg = orient3d(tdest, tapex, toppo, searchpt); 
    oridest = orient3d(tapex, torg, toppo, searchpt);
    oriapex = orient3d(torg, tdest, toppo, searchpt);

    // Now decide which face to move. It is possible there are more than one
    //   faces are viable moves. If so, randomly choose one.
    if (oriorg < 0) {
      if (oridest < 0) {
        if (oriapex < 0) {
          // All three faces are possible.
          s = randomnation(3); // 's' is in {0,1,2}.
          if (s == 0) {
            nextmove = ORGMOVE;
          } else if (s == 1) {
            nextmove = DESTMOVE;
          } else {
            nextmove = APEXMOVE;
          }
        } else {
          // Two faces, opposite to origin and destination, are viable.
          //s = randomnation(2); // 's' is in {0,1}.
          if (randomnation(2)) {
            nextmove = ORGMOVE;
          } else {
            nextmove = DESTMOVE;
          }
        }
      } else {
        if (oriapex < 0) {
          // Two faces, opposite to origin and apex, are viable.
          //s = randomnation(2); // 's' is in {0,1}.
          if (randomnation(2)) {
            nextmove = ORGMOVE;
          } else {
            nextmove = APEXMOVE;
          }
        } else {
          // Only the face opposite to origin is viable.
          nextmove = ORGMOVE;
        }
      }
    } else {
      if (oridest < 0) {
        if (oriapex < 0) {
          // Two faces, opposite to destination and apex, are viable.
          //s = randomnation(2); // 's' is in {0,1}.
          if (randomnation(2)) {
            nextmove = DESTMOVE;
          } else {
            nextmove = APEXMOVE;
          }
        } else {
          // Only the face opposite to destination is viable.
          nextmove = DESTMOVE;
        }
      } else {
        if (oriapex < 0) {
          // Only the face opposite to apex is viable.
          nextmove = APEXMOVE;
        } else {
          // The point we seek must be on the boundary of or inside this
          //   tetrahedron. Check for boundary cases.
          if (oriorg == 0) {
            // Go to the face opposite to origin.
            enextesymself(*searchtet);
            if (oridest == 0) {
              eprevself(*searchtet); // edge oppo->apex
              if (oriapex == 0) {
                // oppo is duplicated with p.
                loc = ONVERTEX; // return ONVERTEX;
                break;
              }
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            if (oriapex == 0) {
              enextself(*searchtet); // edge dest->oppo
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            loc = ONFACE; // return ONFACE;
            break;
          }
          if (oridest == 0) {
            // Go to the face opposite to destination.
            eprevesymself(*searchtet);
            if (oriapex == 0) {
              eprevself(*searchtet); // edge oppo->org
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            loc = ONFACE; // return ONFACE;
            break;
          }
          if (oriapex == 0) {
            // Go to the face opposite to apex
            esymself(*searchtet);
            loc = ONFACE; // return ONFACE;
            break;
          }
          loc = INTETRAHEDRON; // return INTETRAHEDRON;
          break;
        }
      }
    }
    
    // Move to the selected face.
    if (nextmove == ORGMOVE) {
      enextesymself(*searchtet);
    } else if (nextmove == DESTMOVE) {
      eprevesymself(*searchtet);
    } else {
      esymself(*searchtet);
    }
    if (chkencflag) {
      // Check if we are walking across a subface.
      if (issubface(*searchtet)) {
        loc = ENCSUBFACE;
        break;
      }
    }
    // Move to the adjacent tetrahedron (maybe a hull tetrahedron).
    fsymself(*searchtet);
    if (oppo(*searchtet) == dummypoint) {
      loc = OUTSIDE; // return OUTSIDE;
      break;
    }

    // Retreat the three vertices of the base face.
    torg = org(*searchtet);
    tdest = dest(*searchtet);
    tapex = apex(*searchtet);

  } // while (true)

  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flippush()    Push a face (possibly will be flipped) into flipstack.      //
//                                                                           //
// The face is marked. The flag is used to check the validity of the face on //
// its popup.  Some other flips may change it already.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flippush(badface*& fstack, triface* flipface)
{
  badface *newflipface;

  if (!facemarked(*flipface)) {
    newflipface = (badface *) flippool->alloc();
    newflipface->tt = *flipface;
    markface(newflipface->tt);
    // Push this face into stack.
    newflipface->nextitem = fstack;
    fstack = newflipface;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrementalflip()    Incrementally flipping to construct DT.              //
//                                                                           //
// Faces need to be checked for flipping are already queued in 'flipstack'.  //
// Return the total number of performed flips.                               //
//                                                                           //
// Comment:  This routine should be only used in the incremental Delaunay    //
// construction.  In other cases, lawsonflip3d() should be used.             // 
//                                                                           //
// If the new point lies outside of the convex hull ('hullflag' is set). The //
// incremental flip algorithm still works as usual.  However, we must ensure //
// that every flip (2-to-3 or 3-to-2) does not create a duplicated (existing)//
// edge or face. Otherwise, the underlying space of the triangulation becomes//
// non-manifold and it is not possible to flip further.                      //
// Thanks to Joerg Rambau and Frank Lutz for helping in this issue.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc)
{
  badface *popface;
  triface fliptets[5], *parytet;
  point *pts, *parypt, pe;
  REAL sign, ori;
  int flipcount = 0;
  int t1ver;
  int i;

  if (b->verbose > 2) {
    printf("      Lawson flip (%ld faces).\n", flippool->items);
  }

  if (hullflag) {
    // 'newpt' lies in the outside of the convex hull. 
    // Mark all hull vertices which are connecting to it.
    popface = flipstack;
    while (popface != nullptr) {
      pts = (point *) popface->tt.tet;
      for (i = 4; i < 8; i++) {
        if ((pts[i] != newpt) && (pts[i] != dummypoint)) {
          if (!pinfected(pts[i])) {
            pinfect(pts[i]);
            cavetetvertlist->newindex((void **) &parypt);
            *parypt = pts[i];
          }
        } 
      }
      popface = popface->nextitem;
    }
  }

  // Loop until the queue is empty.
  while (flipstack != nullptr) {

    // Pop a face from the stack.
    popface = flipstack;
    fliptets[0] = popface->tt;
    flipstack = flipstack->nextitem; // The next top item in stack.
    flippool->dealloc((void *) popface);

    // Skip it if it is a dead tet (destroyed by previous flips).
    if (isdeadtet(fliptets[0])) continue;
    // Skip it if it is not the same tet as we saved.
    if (!facemarked(fliptets[0])) continue;

    unmarkface(fliptets[0]);    

    if ((point) fliptets[0].tet[7] == dummypoint) {
      // It must be a hull edge.
      fliptets[0].ver = epivot[fliptets[0].ver];
      // A hull edge. The current convex hull may be enlarged.
      fsym(fliptets[0], fliptets[1]);
      pts = (point *) fliptets[1].tet;
      ori = orient3d(pts[4], pts[5], pts[6], newpt);
      if (ori < 0) {
        // Visible. The convex hull will be enlarged.
        // Decide which flip (2-to-3, 3-to-2, or 4-to-1) to use.
        // Check if the tet [a,c,e,d] or [c,b,e,d] exists.
        enext(fliptets[1], fliptets[2]); 
        eprev(fliptets[1], fliptets[3]); 
        fnextself(fliptets[2]); // [a,c,e,*]
        fnextself(fliptets[3]); // [c,b,e,*]
        if (oppo(fliptets[2]) == newpt) {
          if (oppo(fliptets[3]) == newpt) {
            // Both tets exist! A 4-to-1 flip is found.
            assert(0);
          } else {
            esym(fliptets[2], fliptets[0]);
            fnext(fliptets[0], fliptets[1]); 
            fnext(fliptets[1], fliptets[2]); 
            // Perform a 3-to-2 flip. Replace edge [c,a] by face [d,e,b].
            // This corresponds to my standard labels, where edge [e,d] is
            //   repalced by face [a,b,c], and a is the new vertex. 
            //   [0] [c,a,d,e] (d = newpt)
            //   [1] [c,a,e,b] (c = dummypoint)
            //   [2] [c,a,b,d]
            flip32(fliptets, 1, fc);
          }
        } else {
          if (oppo(fliptets[3]) == newpt) {
            fnext(fliptets[3], fliptets[0]);
            fnext(fliptets[0], fliptets[1]); 
            fnext(fliptets[1], fliptets[2]); 
            // Perform a 3-to-2 flip. Replace edge [c,b] by face [d,a,e].
            //   [0] [c,b,d,a] (d = newpt)
            //   [1] [c,b,a,e] (c = dummypoint)
            //   [2] [c,b,e,d]
            flip32(fliptets, 1, fc);
          } else {
            if (hullflag) {
              // Reject this flip if pe is already marked.
              pe = oppo(fliptets[1]);
              if (!pinfected(pe)) {
                pinfect(pe);
                cavetetvertlist->newindex((void **) &parypt);
                *parypt = pe;
                // Perform a 2-to-3 flip.
                flip23(fliptets, 1, fc);
              } else {
                // Reject this flip.
                flipcount--;
              }
            } else {
              // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d].
              //   [0] [a,b,c,d], d = newpt.
              //   [1] [b,a,c,e], c = dummypoint.
              flip23(fliptets, 1, fc);
            }
          }
        }
        flipcount++;
      } else if (ori == 0) {
        if (oppo(fliptets[1]) != newpt) {
          // A coplanar convex hull face!
          // Treat it as unflippable. 
        } else {
          // Two identical hull faces were bonded together!
          assert(0);
        }
      } // ori
      continue;
    } // if (dummypoint)

    fsym(fliptets[0], fliptets[1]);
    if ((point) fliptets[1].tet[7] == dummypoint) {
      // A hull face is locally Delaunay.
      continue;
    }
    // Check if the adjacent tet has already been tested.
    if (marktested(fliptets[1])) {
      // It has been tested and it is Delaunay.
      continue;
    }

    // Test whether the face is locally Delaunay or not.
    pts = (point *) fliptets[1].tet; 
    if (b->weighted) {
      sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt,
                        pts[4][3], pts[5][3], pts[6][3], pts[7][3],
                        newpt[3]);
    } else {
      sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt);
    }


    if (sign < 0) {
      point pd = newpt;
      point pe = oppo(fliptets[1]);
      // Check the convexity of its three edges. Stop checking either a
      //   locally non-convex edge (ori < 0) or a flat edge (ori = 0) is
      //   encountered, and 'fliptet' represents that edge.
      for (i = 0; i < 3; i++) {
        ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe);
        if (ori <= 0) break;
        enextself(fliptets[0]);
      }
      if (ori > 0) {
        // A 2-to-3 flip is found.
        //   [0] [a,b,c,d], 
        //   [1] [b,a,c,e]. no dummypoint.
        flip23(fliptets, 0, fc);
        flipcount++;
      } else { // ori <= 0
        // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat,
        //   where the edge [a',b'] is one of [a,b], [b,c], and [c,a].
        // Check if there are three or four tets sharing at this edge.        
        esymself(fliptets[0]); // [b,a,d,c]
        for (i = 0; i < 3; i++) {
          fnext(fliptets[i], fliptets[i+1]);
        }
        if (fliptets[3].tet == fliptets[0].tet) {
          // A 3-to-2 flip is found. (No hull tet.)
          flip32(fliptets, 0, fc); 
          flipcount++;
        } else {
          // There are more than 3 tets at this edge.
          fnext(fliptets[3], fliptets[4]);
          if (fliptets[4].tet == fliptets[0].tet) {
            if (ori == 0) {
              // A 4-to-4 flip is found. (Two hull tets may be involved.)
              // Current tets in 'fliptets':
              //   [0] [b,a,d,c] (d may be newpt)
              //   [1] [b,a,c,e]
              //   [2] [b,a,e,f] (f may be dummypoint)
              //   [3] [b,a,f,d]
              esymself(fliptets[0]); // [a,b,c,d] 
              // A 2-to-3 flip replaces face [a,b,c] by edge [e,d].
              //   This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]).
              //   It will be removed by the followed 3-to-2 flip.
              flip23(fliptets, 0, fc); // No hull tet.
              fnext(fliptets[3], fliptets[1]);
              fnext(fliptets[1], fliptets[2]);
              // Current tets in 'fliptets':
              //   [0] [...]
              //   [1] [b,a,d,e] (degenerated, d may be new point).
              //   [2] [b,a,e,f] (f may be dummypoint)
              //   [3] [b,a,f,d]
              // A 3-to-2 flip replaces edge [b,a] by face [d,e,f].
              //   Hull tets may be involved (f may be dummypoint).
              flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc);
              flipcount++;
            }
          }
        }
      } // ori
    } else {
      // The adjacent tet is Delaunay. Mark it to avoid testing it again.
      marktest(fliptets[1]);
      // Save it for unmarking it later.
      cavebdrylist->newindex((void **) &parytet);
      *parytet = fliptets[1];
    }

  } // while (flipstack)

  // Unmark saved tetrahedra.
  for (i = 0; i < cavebdrylist->objects; i++) {
    parytet = (triface *) fastlookup(cavebdrylist, i);
    unmarktest(*parytet);
  }
  cavebdrylist->restart();

  if (hullflag) {
    // Unmark infected vertices.
    for (i = 0; i < cavetetvertlist->objects; i++) {
      parypt = (point *) fastlookup(cavetetvertlist, i);
      puninfect(*parypt);
    }
    cavetetvertlist->restart();
  }


  return flipcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initialdelaunay()    Create an initial Delaunay tetrahedralization.       //
//                                                                           //
// The tetrahedralization contains only one tetrahedron abcd, and four hull  //
// tetrahedra. The points pa, pb, pc, and pd must be linearly independent.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd)
{
  triface firsttet, tetopa, tetopb, tetopc, tetopd;
  triface worktet, worktet1;

  if (b->verbose > 2) {
    printf("      Create init tet (%d, %d, %d, %d)\n", pointmark(pa),
           pointmark(pb), pointmark(pc), pointmark(pd));
  }

  // Create the first tetrahedron.
  maketetrahedron(&firsttet);
  setvertices(firsttet, pa, pb, pc, pd);
  // Create four hull tetrahedra.
  maketetrahedron(&tetopa);
  setvertices(tetopa, pb, pc, pd, dummypoint);
  maketetrahedron(&tetopb);
  setvertices(tetopb, pc, pa, pd, dummypoint);
  maketetrahedron(&tetopc);
  setvertices(tetopc, pa, pb, pd, dummypoint);
  maketetrahedron(&tetopd);
  setvertices(tetopd, pb, pa, pc, dummypoint);
  hullsize += 4;

  // Connect hull tetrahedra to firsttet (at four faces of firsttet).
  bond(firsttet, tetopd);
  esym(firsttet, worktet);
  bond(worktet, tetopc); // ab
  enextesym(firsttet, worktet);
  bond(worktet, tetopa); // bc 
  eprevesym(firsttet, worktet);
  bond(worktet, tetopb); // ca

  // Connect hull tetrahedra together (at six edges of firsttet).
  esym(tetopc, worktet); 
  esym(tetopd, worktet1);
  bond(worktet, worktet1); // ab
  esym(tetopa, worktet);
  eprevesym(tetopd, worktet1);
  bond(worktet, worktet1); // bc
  esym(tetopb, worktet);
  enextesym(tetopd, worktet1);
  bond(worktet, worktet1); // ca
  eprevesym(tetopc, worktet);
  enextesym(tetopb, worktet1);
  bond(worktet, worktet1); // da
  eprevesym(tetopa, worktet);
  enextesym(tetopc, worktet1);
  bond(worktet, worktet1); // db
  eprevesym(tetopb, worktet);
  enextesym(tetopa, worktet1);
  bond(worktet, worktet1); // dc

  // Set the vertex type.
  if (pointtype(pa) == UNUSEDVERTEX) {
    setpointtype(pa, VOLVERTEX);
  }
  if (pointtype(pb) == UNUSEDVERTEX) {
    setpointtype(pb, VOLVERTEX);
  }
  if (pointtype(pc) == UNUSEDVERTEX) {
    setpointtype(pc, VOLVERTEX);
  }
  if (pointtype(pd) == UNUSEDVERTEX) {
    setpointtype(pd, VOLVERTEX);
  }

  setpoint2tet(pa, encode(firsttet));
  setpoint2tet(pb, encode(firsttet));
  setpoint2tet(pc, encode(firsttet));
  setpoint2tet(pd, encode(firsttet));

  // Remember the first tetrahedron.
  recenttet = firsttet;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrementaldelaunay()    Create a Delaunay tetrahedralization by          //
//                          the incremental approach.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


void tetgenmesh::incrementaldelaunay(clock_t& tv)
{
  triface searchtet;
  point *permutarray, swapvertex;
  REAL v1[3], v2[3], n[3];
  REAL bboxsize, bboxsize2, bboxsize3, ori;
  int randindex; 
  int ngroup = 0;
  int i, j;

  if (!b->quiet) {
    printf("Delaunizing vertices...\n");
  }

  // Form a random permuation (uniformly at random) of the set of vertices.
  permutarray = new point[in->numberofpoints];
  points->traversalinit();

  if (b->no_sort) {
    if (b->verbose) {
      printf("  Using the input order.\n"); 
    }
    for (i = 0; i < in->numberofpoints; i++) {
      permutarray[i] = (point) points->traverse();
    }
  } else {
    if (b->verbose) {
      printf("  Permuting vertices.\n"); 
    }
    srand(in->numberofpoints);
    for (i = 0; i < in->numberofpoints; i++) {
      randindex = rand() % (i + 1); // randomnation(i + 1);
      permutarray[i] = permutarray[randindex];
      permutarray[randindex] = (point) points->traverse();
    }
    if (b->brio_hilbert) { // -b option
      if (b->verbose) {
        printf("  Sorting vertices.\n"); 
      }
      hilbert_init(in->mesh_dim);
      brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold, 
                           b->brio_ratio, &ngroup);
    }
  }

  tv = clock(); // Remember the time for sorting points.

  // Calculate the diagonal size of its bounding box.
  bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin));
  bboxsize2 = bboxsize * bboxsize;
  bboxsize3 = bboxsize2 * bboxsize;

  // Make sure the second vertex is not identical with the first one.
  i = 1;
  while ((distance(permutarray[0],permutarray[i])/bboxsize)<b->epsilon) {
    i++;
    if (i == in->numberofpoints - 1) {
      printf("Exception:  All vertices are (nearly) identical (Tol = %g).\n",
             b->epsilon);
      terminatetetgen(10);
    }
  }
  if (i > 1) {
    // Swap to move the non-indetical vertex from index i to index 1.
    swapvertex = permutarray[i];
    permutarray[i] = permutarray[1];
    permutarray[1] = swapvertex;
  }

  // Make sure the third vertex is not collinear with the first two.
  i = 2;
  for (j = 0; j < 3; j++) {
    v1[j] = permutarray[1][j] - permutarray[0][j];
    v2[j] = permutarray[i][j] - permutarray[0][j];
  }
  cross(v1, v2, n);
  while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < b->epsilon) {
    i++;
    if (i == in->numberofpoints - 1) {
      printf("Exception:  All vertices are (nearly) collinear (Tol = %g).\n",
             b->epsilon);
      terminatetetgen(10);
    }
    for (j = 0; j < 3; j++) {
      v2[j] = permutarray[i][j] - permutarray[0][j];
    }
    cross(v1, v2, n);
  }
  if (i > 2) {
    // Swap to move the non-indetical vertex from index i to index 1.
    swapvertex = permutarray[i];
    permutarray[i] = permutarray[2];
    permutarray[2] = swapvertex;
  }

  // Make sure the fourth vertex is not coplanar with the first three.
  i = 3;
  ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], 
                     permutarray[i]);
  while ((fabs(ori) / bboxsize3) < b->epsilon) {
    i++;
    if (i == in->numberofpoints) {
      printf("Exception:  All vertices are coplanar (Tol = %g).\n",
             b->epsilon);
      terminatetetgen(10);
    }
    ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], 
                       permutarray[i]);
  }
  if (i > 3) {
    // Swap to move the non-indetical vertex from index i to index 1.
    swapvertex = permutarray[i];
    permutarray[i] = permutarray[3];
    permutarray[3] = swapvertex;
  }

  // Orient the first four vertices in permutarray so that they follow the
  //   right-hand rule.
  if (ori > 0.0) {
    // Swap the first two vertices.
    swapvertex = permutarray[0];
    permutarray[0] = permutarray[1];
    permutarray[1] = swapvertex;
  }

  // Create the initial Delaunay tetrahedralization.
  initialdelaunay(permutarray[0], permutarray[1], permutarray[2],
                  permutarray[3]);

  if (b->verbose) {
    printf("  Incrementally inserting vertices.\n");
  }
  insertvertexflags ivf;
  flipconstraints fc;
  // int loc;

  // Choose algorithm: Bowyer-Watson (default) or Incremental Flip
  if (b->incrflip) {
    ivf.bowywat = 0;
    ivf.lawson = 1;
    fc.enqflag = 1;
  } else {
    ivf.bowywat = 1;
    ivf.lawson = 0;
  }


  for (i = 4; i < in->numberofpoints; i++) {
    if (pointtype(permutarray[i]) == UNUSEDVERTEX) {
      setpointtype(permutarray[i], VOLVERTEX);
    }
    if (b->brio_hilbert || b->no_sort) { // -b or -b/1
      // Start the last updated tet.
      searchtet.tet = recenttet.tet;
    } else { // -b0
      // Randomly choose the starting tet for point location.
      searchtet.tet = nullptr;
    }
    ivf.iloc = (int) OUTSIDE;
    // Insert the vertex.
    if (insertpoint(permutarray[i], &searchtet, nullptr, nullptr, &ivf)) {
      if (flipstack != nullptr) {
        // Perform flip to recover Delaunayness.
        incrementalflip(permutarray[i], (ivf.iloc == (int) OUTSIDE), &fc);
      }
    } else {
      if (ivf.iloc == (int) ONVERTEX) {
        // The point already exists. Mark it and do nothing on it.
        swapvertex = org(searchtet);
        assert(swapvertex != permutarray[i]); // SELF_CHECK
        if (b->object != tetgenbehavior::STL) {
          if (!b->quiet) {
            printf("Warning:  Point #%d is coincident with #%d. Ignored!\n",
                   pointmark(permutarray[i]), pointmark(swapvertex));
          }
        }
        setpoint2ppt(permutarray[i], swapvertex);
        setpointtype(permutarray[i], DUPLICATEDVERTEX);
        dupverts++;
      } else if (ivf.iloc == (int) NEARVERTEX) {
        swapvertex = point2ppt(permutarray[i]);
        if (!b->quiet) {
          printf("Warning:  Point %d is replaced by point %d.\n",
                 pointmark(permutarray[i]), pointmark(swapvertex));
          printf("  Avoid creating a very short edge (len = %g) (< %g).\n",
                 permutarray[i][3], b->minedgelength);
          printf("  You may try a smaller tolerance (-T) (current is %g)\n", 
                 b->epsilon);
          printf("  or use the option -M0/1 to avoid such replacement.\n");
        }
        // Remember it is a duplicated point.
        setpointtype(permutarray[i], DUPLICATEDVERTEX);
        // Count the number of duplicated points.
        dupverts++;
      }
    }
  }



  delete [] permutarray;
}

////                                                                       ////
////                                                                       ////
//// delaunay_cxx /////////////////////////////////////////////////////////////

//// surface_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipshpush()    Push a facet edge into flip stack.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flipshpush(face* flipedge)
{
  badface *newflipface;

  newflipface = (badface *) flippool->alloc();
  newflipface->ss = *flipedge;
  newflipface->forg = sorg(*flipedge);
  newflipface->fdest = sdest(*flipedge);
  newflipface->nextitem = flipstack;
  flipstack = newflipface;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip22()    Perform a 2-to-2 flip in surface mesh.                        //
//                                                                           //
// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and   //
// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] //
// is replaced by edge [c,d].                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag)
{
  face bdedges[4], outfaces[4], infaces[4];
  face bdsegs[4];
  face checkface;
  point pa, pb, pc, pd;
  int i;

  pa = sorg(flipfaces[0]);
  pb = sdest(flipfaces[0]);
  pc = sapex(flipfaces[0]);
  pd = sapex(flipfaces[1]);

  if (sorg(flipfaces[1]) != pb) {
    sesymself(flipfaces[1]);
  }

  flip22count++;

  // Collect the four boundary edges.
  senext(flipfaces[0], bdedges[0]);
  senext2(flipfaces[0], bdedges[1]);
  senext(flipfaces[1], bdedges[2]);
  senext2(flipfaces[1], bdedges[3]);

  // Collect outer boundary faces.
  for (i = 0; i < 4; i++) {
    spivot(bdedges[i], outfaces[i]);
    infaces[i] = outfaces[i];
    sspivot(bdedges[i], bdsegs[i]);
    if (outfaces[i].sh != nullptr) {
      if (isshsubseg(bdedges[i])) {
        spivot(infaces[i], checkface);
        while (checkface.sh != bdedges[i].sh) {
          infaces[i] = checkface;
          spivot(infaces[i], checkface);
        }
      }
    }
  }

  // The flags set in these two subfaces do not change.
  // Shellmark does not change.
  // area constraint does not change.

  // Transform [a,b,c] -> [c,d,b].
  setshvertices(flipfaces[0], pc, pd, pb);
  // Transform [b,a,d] -> [d,c,a].
  setshvertices(flipfaces[1], pd, pc, pa);

  // Update the point-to-subface map.
  if (pointtype(pa) == FREEFACETVERTEX) {
    setpoint2sh(pa, sencode(flipfaces[1]));
  }
  if (pointtype(pb) == FREEFACETVERTEX) {
    setpoint2sh(pb, sencode(flipfaces[0]));
  }
  if (pointtype(pc) == FREEFACETVERTEX) {
    setpoint2sh(pc, sencode(flipfaces[0]));
  }
  if (pointtype(pd) == FREEFACETVERTEX) {
    setpoint2sh(pd, sencode(flipfaces[0]));
  }

  // Reconnect boundary edges to outer boundary faces.
  for (i = 0; i < 4; i++) {
    if (outfaces[(3 + i) % 4].sh != nullptr) {
      // Make sure that the subface has the ori as the segment.
      if (bdsegs[(3 + i) % 4].sh != nullptr) {
        bdsegs[(3 + i) % 4].shver = 0;
        if (sorg(bdedges[i]) != sorg(bdsegs[(3 + i) % 4])) {
          sesymself(bdedges[i]);
        }
      }
      sbond1(bdedges[i], outfaces[(3 + i) % 4]);
      sbond1(infaces[(3 + i) % 4], bdedges[i]);
    } else {
      sdissolve(bdedges[i]);
    }
    if (bdsegs[(3 + i) % 4].sh != nullptr) {
      ssbond(bdedges[i], bdsegs[(3 + i) % 4]);
      if (chkencflag & 1) {
        // Queue this segment for encroaching check.
        enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4]));
      }
    } else {
      ssdissolve(bdedges[i]);
    }
  }

  if (chkencflag & 2) {
    // Queue the flipped subfaces for quality/encroaching checks.
    for (i = 0; i < 2; i++) {
      enqueuesubface(badsubfacs, &(flipfaces[i]));
    }
  }

  recentsh = flipfaces[0];

  if (flipflag) {
    // Put the boundary edges into flip stack.
    for (i = 0; i < 4; i++) {
      flipshpush(&(bdedges[i]));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip31()    Remove a vertex by transforming 3-to-1 subfaces.              //
//                                                                           //
// 'flipfaces' is an array of subfaces. Its length is at least 4.  On input, //
// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine    //
// replaces them by one face [a,b,c], it is returned in flipfaces[3].        //
//                                                                           //
// NOTE: The three old subfaces are not deleted within this routine.  They   //
// still hold pointers to their adjacent subfaces. These informations are    //
// needed by the routine 'sremovevertex()' for recovering a segment.         //
// The caller of this routine must delete the old subfaces after their uses. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip31(face* flipfaces, int flipflag)
{
  face bdedges[3], outfaces[3], infaces[3];
  face bdsegs[3];
  face checkface;
  point pa, pb, pc;
  int i;

  pa = sdest(flipfaces[0]);
  pb = sdest(flipfaces[1]);
  pc = sdest(flipfaces[2]);

  flip31count++;

  // Collect all infos at the three boundary edges.
  for (i = 0; i < 3; i++) {
    senext(flipfaces[i], bdedges[i]);
    spivot(bdedges[i], outfaces[i]);
    infaces[i] = outfaces[i];
    sspivot(bdedges[i], bdsegs[i]);
    if (outfaces[i].sh != nullptr) {
      if (isshsubseg(bdedges[i])) {
        spivot(infaces[i], checkface);
        while (checkface.sh != bdedges[i].sh) {
          infaces[i] = checkface;
          spivot(infaces[i], checkface);
        }
      }
    }
  } // i

  // Create a new subface.
  makeshellface(subfaces, &(flipfaces[3]));
  setshvertices(flipfaces[3], pa, pb,pc);
  setshellmark(flipfaces[3], shellmark(flipfaces[0]));
  if (checkconstraints) {
    //area = areabound(flipfaces[0]);
    setareabound(flipfaces[3], areabound(flipfaces[0]));
  }

  // Update the point-to-subface map.
  if (pointtype(pa) == FREEFACETVERTEX) {
    setpoint2sh(pa, sencode(flipfaces[3]));
  }
  if (pointtype(pb) == FREEFACETVERTEX) {
    setpoint2sh(pb, sencode(flipfaces[3]));
  }
  if (pointtype(pc) == FREEFACETVERTEX) {
    setpoint2sh(pc, sencode(flipfaces[3]));
  }

  // Update the three new boundary edges.
  bdedges[0] = flipfaces[3];         // [a,b]
  senext(flipfaces[3], bdedges[1]);  // [b,c]
  senext2(flipfaces[3], bdedges[2]); // [c,a]

  // Reconnect boundary edges to outer boundary faces.
  for (i = 0; i < 3; i++) {
    if (outfaces[i].sh != nullptr) {
      // Make sure that the subface has the ori as the segment.
      if (bdsegs[i].sh != nullptr) {
        bdsegs[i].shver = 0;
        if (sorg(bdedges[i]) != sorg(bdsegs[i])) {
          sesymself(bdedges[i]);
        }
      }
      sbond1(bdedges[i], outfaces[i]);
      sbond1(infaces[i], bdedges[i]);
    }
    if (bdsegs[i].sh != nullptr) {
      ssbond(bdedges[i], bdsegs[i]);
    }
  }

  recentsh = flipfaces[3];

  if (flipflag) {
    // Put the boundary edges into flip stack.
    for (i = 0; i < 3; i++) {
      flipshpush(&(bdedges[i]));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lawsonflip()    Flip non-locally Delaunay edges.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::lawsonflip()
{
  badface *popface;
  face flipfaces[2];
  point pa, pb, pc, pd;
  REAL sign;
  long flipcount = 0;

  if (b->verbose > 2) {
    printf("      Lawson flip %ld edges.\n", flippool->items);
  }

  while (flipstack != (badface *) nullptr) {

    // Pop an edge from the stack.
    popface = flipstack;
    flipfaces[0] = popface->ss;
    pa = popface->forg;
    pb = popface->fdest;
    flipstack = popface->nextitem; // The next top item in stack.
    flippool->dealloc((void *) popface);

    // Skip it if it is dead.
    if (flipfaces[0].sh[3] == nullptr) continue;
    // Skip it if it is not the same edge as we saved.
    if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue;
    // Skip it if it is a subsegment.
    if (isshsubseg(flipfaces[0])) continue;

    // Get the adjacent face.
    spivot(flipfaces[0], flipfaces[1]);
    if (flipfaces[1].sh == nullptr) continue; // Skip a hull edge.
    pc = sapex(flipfaces[0]);
    pd = sapex(flipfaces[1]);

    sign = incircle3d(pa, pb, pc, pd);

    if (sign < 0) {
      // It is non-locally Delaunay. Flip it.
      flip22(flipfaces, 1, 0);
      flipcount++;
    }
  }

  if (b->verbose > 2) {
    printf("      Performed %ld flips.\n", flipcount);
  }

  return flipcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sinsertvertex()    Insert a vertex into a triangulation of a facet.       //
//                                                                           //
// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and //
// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p),   //
// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a  //
// segment, 'cavesegshlist' returns the two new subsegments.                 //
//                                                                           //
// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine //
// will first locate the point. It starts searching from 'searchsh' or 'rec- //
// entsh' if 'searchsh' is nullptr.                                             //
//                                                                           //
// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert    //
// the vertex. Otherwise, only insert the vertex in the initial cavity.      // 
//                                                                           //
// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already   //
// provided in the list 'caveshlist'.                                        //
//                                                                           //
// If 'splitseg' is not nullptr, the new vertex lies on the segment and it will //
// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'.                     //
//                                                                           //
// 'rflag' (rounding) is a parameter passed to slocate() function.  If it is //
// set, after the location of the point is found, either ONEDGE or ONFACE,   //
// round the result using an epsilon.                                        //
//                                                                           //
// NOTE: the old subfaces in C(p) are not deleted. Theyare needed in case we //
// want to remove the new point immedately.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg,
                              int iloc, int bowywat, int rflag)
{
  face cavesh, neighsh, *parysh;
  face newsh, casout, casin;
  face checkseg;
  point pa, pb;
  enum locateresult loc = OUTSIDE;
  REAL sign, ori;
  int i, j;

  if (b->verbose > 2) {
    printf("      Insert facet point %d.\n", pointmark(insertpt));
  }

  if (bowywat == 3) {
    loc = INSTAR;
  }

  if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
    // A segment is going to be split, no point location.
    spivot(*splitseg, *searchsh);
    if (loc != INSTAR) loc = ONEDGE;
  } else {
    if (loc != INSTAR) loc = (enum locateresult) iloc;
    if (loc == OUTSIDE) {
      // Do point location in surface mesh.
      if (searchsh->sh == nullptr) {
        *searchsh = recentsh;
      }
      // Search the vertex. An above point must be provided ('aflag' = 1).
      loc = slocate(insertpt, searchsh, 1, 1, rflag);
    }
  }


  // Form the initial sC(p).
  if (loc == ONFACE) {
    // Add the face into list (in B-W cavity).
    smarktest(*searchsh);
    caveshlist->newindex((void **) &parysh);
    *parysh = *searchsh;
  } else if (loc == ONEDGE) {
    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      splitseg->shver = 0;
      pa = sorg(*splitseg);
    } else {
      pa = sorg(*searchsh);
    }
    if (searchsh->sh != nullptr) {
      // Collect all subfaces share at this edge.
      neighsh = *searchsh;
      while (1) {
        // Adjust the origin of its edge to be 'pa'.
        if (sorg(neighsh) != pa) sesymself(neighsh);
        // Add this face into list (in B-W cavity).
        smarktest(neighsh);
        caveshlist->newindex((void **) &parysh);
        *parysh = neighsh;
        // Add this face into face-at-splitedge list.
        cavesegshlist->newindex((void **) &parysh);
        *parysh = neighsh;
        // Go to the next face at the edge.
        spivotself(neighsh);
        // Stop if all faces at the edge have been visited.
        if (neighsh.sh == searchsh->sh) break;
        if (neighsh.sh == nullptr) break;
      }
    } // If (not a non-dangling segment).
  } else if (loc == ONVERTEX) {
    return (int) loc;
  } else if (loc == OUTSIDE) {
    // Comment: This should only happen during the surface meshing step.
    // Enlarge the convex hull of the triangulation by including p.
    // An above point of the facet is set in 'dummypoint' to replace
    // orient2d tests by orient3d tests.
    // Imagine that the current edge a->b (in 'searchsh') is horizontal in a
    //   plane, and a->b is directed from left to right, p lies above a->b.  
    //   Find the right-most edge of the triangulation which is visible by p.
    neighsh = *searchsh;
    while (1) {
      senext2self(neighsh);
      spivot(neighsh, casout);
      if (casout.sh == nullptr) {
        // A convex hull edge. Is it visible by p.
        ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt);
        if (ori < 0) {
          *searchsh = neighsh; // Visible, update 'searchsh'.
        } else {
          break; // 'searchsh' is the right-most visible edge.
        }
      } else {
        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
        neighsh = casout;
      }
    }
    // Create new triangles for all visible edges of p (from right to left).
    casin.sh = nullptr;  // No adjacent face at right.
    pa = sorg(*searchsh);
    pb = sdest(*searchsh);
    while (1) {
      // Create a new subface on top of the (visible) edge.
      makeshellface(subfaces, &newsh); 
      setshvertices(newsh, pb, pa, insertpt);
      setshellmark(newsh, shellmark(*searchsh));
      if (checkconstraints) {
        //area = areabound(*searchsh);
        setareabound(newsh, areabound(*searchsh));
      }
      // Connect the new subface to the bottom subfaces.
      sbond1(newsh, *searchsh);
      sbond1(*searchsh, newsh);
      // Connect the new subface to its right-adjacent subface.
      if (casin.sh != nullptr) {
        senext(newsh, casout);
        sbond1(casout, casin);
        sbond1(casin, casout);
      }
      // The left-adjacent subface has not been created yet.
      senext2(newsh, casin);
      // Add the new face into list (inside the B-W cavity).
      smarktest(newsh);
      caveshlist->newindex((void **) &parysh);
      *parysh = newsh;
      // Move to the convex hull edge at the left of 'searchsh'.
      neighsh = *searchsh;
      while (1) {
        senextself(neighsh);
        spivot(neighsh, casout);
        if (casout.sh == nullptr) {
          *searchsh = neighsh;
          break;
        }
        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
        neighsh = casout;
      }
      // A convex hull edge. Is it visible by p.
      pa = sorg(*searchsh);
      pb = sdest(*searchsh);
      ori = orient3d(pa, pb, dummypoint, insertpt);
      // Finish the process if p is not visible by the hull edge.
      if (ori >= 0) break;
    }
  } else if (loc == INSTAR) {
    // Under this case, the sub-cavity sC(p) has already been formed in
    //   insertvertex().
  }

  // Form the Bowyer-Watson cavity sC(p).
  for (i = 0; i < caveshlist->objects; i++) {
    cavesh = * (face *) fastlookup(caveshlist, i);
    for (j = 0; j < 3; j++) {
      if (!isshsubseg(cavesh)) {
        spivot(cavesh, neighsh);
        if (neighsh.sh != nullptr) {
          // The adjacent face exists.
          if (!smarktested(neighsh)) {
            if (bowywat) {
              if (loc == INSTAR) { // if (bowywat > 2) {
                // It must be a boundary edge.
                sign = 1;
              } else {
                // Check if this subface is connected to adjacent tet(s).
                if (!isshtet(neighsh)) {
                  // Check if the subface is non-Delaunay wrt. the new pt.
                  sign = incircle3d(sorg(neighsh), sdest(neighsh), 
                                    sapex(neighsh), insertpt);
                } else {
                  // It is connected to an adjacent tet. A boundary edge.
                  sign = 1;
                }
              }
              if (sign < 0) {
                // Add the adjacent face in list (in B-W cavity).
                smarktest(neighsh);
                caveshlist->newindex((void **) &parysh);
                *parysh = neighsh;
              }
            } else {
              sign = 1; // A boundary edge.
            }
          } else {
            sign = -1; // Not a boundary edge.
          }
        } else {
          // No adjacent face. It is a hull edge.
          if (loc == OUTSIDE) {
            // It is a boundary edge if it does not contain p.
            if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) {
              sign = -1; // Not a boundary edge.
            } else {
              sign = 1; // A boundary edge.
            }
          } else {
            sign = 1; // A boundary edge.
          }
        }
      } else {
        // Do not across a segment. It is a boundary edge.
        sign = 1;
      }
      if (sign >= 0) {
        // Add a boundary edge.
        caveshbdlist->newindex((void **) &parysh);
        *parysh = cavesh;
      }
      senextself(cavesh);
    } // j
  } // i


  // Creating new subfaces.
  for (i = 0; i < caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    sspivot(*parysh, checkseg);
    if ((parysh->shver & 01) != 0) sesymself(*parysh);
    pa = sorg(*parysh);
    pb = sdest(*parysh);
    // Create a new subface.
    makeshellface(subfaces, &newsh); 
    setshvertices(newsh, pa, pb, insertpt);
    setshellmark(newsh, shellmark(*parysh));
    setshelltype(newsh, shelltype(*parysh));
    if (checkconstraints) {
      //area = areabound(*parysh);
      setareabound(newsh, areabound(*parysh));
    }
    // Update the point-to-subface map.
    if (pointtype(pa) == FREEFACETVERTEX) {
      setpoint2sh(pa, sencode(newsh));
    }
    if (pointtype(pb) == FREEFACETVERTEX) {
      setpoint2sh(pb, sencode(newsh));
    }
    // Connect newsh to outer subfaces.
    spivot(*parysh, casout);
    if (casout.sh != nullptr) {
      casin = casout;
      if (checkseg.sh != nullptr) {
        // Make sure that newsh has the right ori at this segment.
        checkseg.shver = 0;
        if (sorg(newsh) != sorg(checkseg)) {
          sesymself(newsh);
          sesymself(*parysh); // This side should also be inversed.
        }
        spivot(casin, neighsh);
        while (neighsh.sh != parysh->sh) {
          casin = neighsh;
          spivot(casin, neighsh);
        }
      }
      sbond1(newsh, casout);
      sbond1(casin, newsh);
    }
    if (checkseg.sh != nullptr) {
      ssbond(newsh, checkseg);
    }
    // Connect oldsh <== newsh (for connecting adjacent new subfaces).
    //   *parysh and newsh point to the same edge and the same ori.
    sbond1(*parysh, newsh);
  }

  if (newsh.sh != nullptr) {
    // Set a handle for searching.
    recentsh = newsh;
  }

  // Update the point-to-subface map.
  if (pointtype(insertpt) == FREEFACETVERTEX) {
    setpoint2sh(insertpt, sencode(newsh));
  }

  // Connect adjacent new subfaces together.
  for (i = 0; i < caveshbdlist->objects; i++) {
    // Get an old subface at edge [a, b].
    parysh = (face *) fastlookup(caveshbdlist, i);
    spivot(*parysh, newsh); // The new subface [a, b, p].
    senextself(newsh); // At edge [b, p].
    spivot(newsh, neighsh);
    if (neighsh.sh == nullptr) {
      // Find the adjacent new subface at edge [b, p].
      pb = sdest(*parysh);
      neighsh = *parysh;
      while (1) {
        senextself(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == nullptr) break;
        if (!smarktested(neighsh)) break;
        if (sdest(neighsh) != pb) sesymself(neighsh);
      }
      if (neighsh.sh != nullptr) {
        // Now 'neighsh' is a new subface at edge [b, #].
        if (sorg(neighsh) != pb) sesymself(neighsh);
        senext2self(neighsh); // Go to the open edge [p, b].
        sbond(newsh, neighsh);
      } else {
        // There is no adjacent new face at this side.
        assert(loc == OUTSIDE); // SELF_CHECK
      }
    }
    spivot(*parysh, newsh); // The new subface [a, b, p].
    senext2self(newsh); // At edge [p, a].
    spivot(newsh, neighsh);
    if (neighsh.sh == nullptr) {
      // Find the adjacent new subface at edge [p, a].
      pa = sorg(*parysh);
      neighsh = *parysh;
      while (1) {
        senext2self(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == nullptr) break;
        if (!smarktested(neighsh)) break;
        if (sorg(neighsh) != pa) sesymself(neighsh);
      }
      if (neighsh.sh != nullptr) {
        // Now 'neighsh' is a new subface at edge [#, a].
        if (sdest(neighsh) != pa) sesymself(neighsh);
        senextself(neighsh); // Go to the open edge [a, p].
        sbond(newsh, neighsh);
      } else {
        // There is no adjacent new face at this side.
        assert(loc == OUTSIDE); // SELF_CHECK
      }
    }
  }

  if ((loc == ONEDGE) || ((splitseg != nullptr) && (splitseg->sh != nullptr))
      || (cavesegshlist->objects > 0l)) {
    // An edge is being split. We distinguish two cases:
    //   (1) the edge is not on the boundary of the cavity;
    //   (2) the edge is on the boundary of the cavity.
    // In case (2), the edge is either a segment or a hull edge. There are
    //   degenerated new faces in the cavity. They must be removed.
    face aseg, bseg, aoutseg, boutseg;

    for (i = 0; i < cavesegshlist->objects; i++) {
      // Get the saved old subface.
      parysh = (face *) fastlookup(cavesegshlist, i);
      // Get a possible new degenerated subface.
      spivot(*parysh, cavesh);
      if (sapex(cavesh) == insertpt) {
        // Found a degenerated new subface, i.e., case (2).
        if (cavesegshlist->objects > 1) {
          // There are more than one subface share at this edge.
          j = (i + 1) % (int) cavesegshlist->objects;
          parysh = (face *) fastlookup(cavesegshlist, j);
          spivot(*parysh, neighsh);
          // Adjust cavesh and neighsh both at edge a->b, and has p as apex.
          if (sorg(neighsh) != sorg(cavesh)) {
            sesymself(neighsh);
            assert(sorg(neighsh) == sorg(cavesh)); // SELF_CHECK
          }
          assert(sapex(neighsh) == insertpt); // SELF_CHECK
          // Connect adjacent faces at two other edges of cavesh and neighsh.
          //   As a result, the two degenrated new faces are squessed from the
          //   new triangulation of the cavity. Note that the squeezed faces
          //   still hold the adjacent informations which will be used in 
          //   re-connecting subsegments (if they exist). 
          for (j = 0; j < 2; j++) { 
            senextself(cavesh);
            senextself(neighsh);
            spivot(cavesh, newsh);
            spivot(neighsh, casout);
            sbond1(newsh, casout); // newsh <- casout.
          }
        } else {
          // There is only one subface containing this edge [a,b]. Squeese the
          //   degenerated new face [a,b,c] by disconnecting it from its two 
          //   adjacent subfaces at edges [b,c] and [c,a]. Note that the face
          //   [a,b,c] still hold the connection to them.
          for (j = 0; j < 2; j++) {
            senextself(cavesh);
            spivot(cavesh, newsh);
            sdissolve(newsh);
          }
        }
        //recentsh = newsh;
        // Update the point-to-subface map.
        if (pointtype(insertpt) == FREEFACETVERTEX) {
          setpoint2sh(insertpt, sencode(newsh));
        }
      }
    }

    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      if (loc != INSTAR) { // if (bowywat < 3) {
        smarktest(*splitseg); // Mark it as being processed.
      }
      
      aseg = *splitseg;
      pa = sorg(*splitseg);
      pb = sdest(*splitseg);

      // Insert the new point p.
      makeshellface(subsegs, &aseg);
      makeshellface(subsegs, &bseg);

      setshvertices(aseg, pa, insertpt, nullptr);
      setshvertices(bseg, insertpt, pb, nullptr);
      setshellmark(aseg, shellmark(*splitseg));
      setshellmark(bseg, shellmark(*splitseg));
      setshelltype(aseg, shelltype(*splitseg));
      setshelltype(bseg, shelltype(*splitseg));
      if (checkconstraints) {
        setareabound(aseg, areabound(*splitseg));
        setareabound(bseg, areabound(*splitseg));
      }

      // Connect [#, a]<->[a, p].
      senext2(*splitseg, boutseg); // Temporarily use boutseg.
      spivotself(boutseg);
      if (boutseg.sh != nullptr) {
        senext2(aseg, aoutseg);
        sbond(boutseg, aoutseg);
      }
      // Connect [p, b]<->[b, #].
      senext(*splitseg, aoutseg);
      spivotself(aoutseg);
      if (aoutseg.sh != nullptr) {
        senext(bseg, boutseg);
        sbond(boutseg, aoutseg);
      }
      // Connect [a, p] <-> [p, b].
      senext(aseg, aoutseg);
      senext2(bseg, boutseg);
      sbond(aoutseg, boutseg);

      // Connect subsegs [a, p] and [p, b] to adjacent new subfaces.
      // Although the degenerated new faces have been squeesed. They still
      //   hold the connections to the actual new faces. 
      for (i = 0; i < cavesegshlist->objects; i++) {        
        parysh = (face *) fastlookup(cavesegshlist, i);
        spivot(*parysh, neighsh);
        // neighsh is a degenerated new face.
        if (sorg(neighsh) != pa) {
          sesymself(neighsh);
        }
        senext2(neighsh, newsh);
        spivotself(newsh); // The edge [p, a] in newsh
        ssbond(newsh, aseg);
        senext(neighsh, newsh);
        spivotself(newsh); // The edge [b, p] in newsh
        ssbond(newsh, bseg);
      }


      // Let the point remember the segment it lies on.
      if (pointtype(insertpt) == FREESEGVERTEX) {
        setpoint2sh(insertpt, sencode(aseg));
      }
      // Update the point-to-seg map.
      if (pointtype(pa) == FREESEGVERTEX) {
        setpoint2sh(pa, sencode(aseg));
      }
      if (pointtype(pb) == FREESEGVERTEX) {
        setpoint2sh(pb, sencode(bseg));
      }
    } // if ((splitseg != nullptr) && (splitseg->sh != nullptr)) 

    // Delete all degenerated new faces.
    for (i = 0; i < cavesegshlist->objects; i++) {
      parysh = (face *) fastlookup(cavesegshlist, i);
      spivotself(*parysh);
      if (sapex(*parysh) == insertpt) {
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
    cavesegshlist->restart();

    if ((splitseg != nullptr) && (splitseg->sh != nullptr)) {
      // Return the two new subsegments (for further process).
      //   Re-use 'cavesegshlist'.
      cavesegshlist->newindex((void **) &parysh);
      *parysh = aseg;
      cavesegshlist->newindex((void **) &parysh);
      *parysh = bseg;
    }
  } // if (loc == ONEDGE)


  return (int) loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sremovevertex()    Remove a vertex from the surface mesh.                 //
//                                                                           //
// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not nullptr, p is //
// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a   //
// facet vertex, and the origin of 'parentsh' is p.                          //
//                                                                           //
// Within each facet, we first use a sequence of 2-to-2 flips to flip any    //
// edge at p, finally use a 3-to-1 flip to remove p.                         //
//                                                                           //
// All new created subfaces are returned in the global array 'caveshbdlist'. //
// The new segment (when p is on segment) is returned in 'parentseg'.        //
//                                                                           //
// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay-   //
// ness after p is removed.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg,
                              int lawson)
{
  face flipfaces[4], spinsh, *parysh;
  point pa, pb, pc, pd;
  REAL ori1, ori2;
  int it, i, j;

  if (parentseg != nullptr) {
    // 'delpt' (p) should be a Steiner point inserted in a segment [a,b],
    //   where 'parentseg' should be [p,b]. Find the segment [a,p].
    face startsh, neighsh, nextsh;
    face abseg, prevseg, checkseg;
    face adjseg1, adjseg2;
    face fakesh;
    senext2(*parentseg, prevseg);
    spivotself(prevseg);
    prevseg.shver = 0;
    assert(sdest(prevseg) == delpt);
    // Restore the original segment [a,b].
    pa = sorg(prevseg);
    pb = sdest(*parentseg);
    if (b->verbose > 2) {
      printf("      Remove vertex %d from segment [%d, %d].\n", 
             pointmark(delpt), pointmark(pa), pointmark(pb));
    }
    makeshellface(subsegs, &abseg);
    setshvertices(abseg, pa, pb, nullptr);
    setshellmark(abseg, shellmark(*parentseg));
    setshelltype(abseg, shelltype(*parentseg));
    if (checkconstraints) {
      setareabound(abseg, areabound(*parentseg));
    }
    // Connect [#, a]<->[a, b].
    senext2(prevseg, adjseg1);
    spivotself(adjseg1);
    if (adjseg1.sh != nullptr) {
      adjseg1.shver = 0;
      assert(sdest(adjseg1) == pa);
      senextself(adjseg1);
      senext2(abseg, adjseg2);
      sbond(adjseg1, adjseg2);
    }
    // Connect [a, b]<->[b, #].
    senext(*parentseg, adjseg1);
    spivotself(adjseg1);
    if (adjseg1.sh != nullptr) {
      adjseg1.shver = 0;
      assert(sorg(adjseg1) == pb);
      senext2self(adjseg1);
      senext(abseg, adjseg2);
      sbond(adjseg1, adjseg2);
    }
    // Update the point-to-segment map.
    setpoint2sh(pa, sencode(abseg));
    setpoint2sh(pb, sencode(abseg));

    // Get the faces in face ring at segment [p, b].
    //   Re-use array 'caveshlist'.
    spivot(*parentseg, *parentsh);
    spinsh = *parentsh;
    while (1) {
      // Save this face in list.
      caveshlist->newindex((void **) &parysh);
      *parysh = spinsh;
      // Go to the next face in the ring.
      spivotself(spinsh);
      if (spinsh.sh == nullptr) break;
      if (spinsh.sh == parentsh->sh) break;
    } 

    // Create the face ring of the new segment [a,b]. Each face in the ring
    //   is [a,b,p] (degenerated!). It will be removed (automatically).
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      startsh = *parysh;
      if (sorg(startsh) != delpt) {
        sesymself(startsh);
        assert(sorg(startsh) == delpt);
      }      
      // startsh is [p, b, #1], find the subface [a, p, #2].
      neighsh = startsh;
      while (1) {
        senext2self(neighsh);
        sspivot(neighsh, checkseg);
        if (checkseg.sh != nullptr) {
          // It must be the segment [a, p].
          assert(checkseg.sh == prevseg.sh);
          break;
        }
        spivotself(neighsh);
        assert(neighsh.sh != nullptr);
        if (sorg(neighsh) != delpt) sesymself(neighsh);
      }
      // Now neighsh is [a, p, #2].
      if (neighsh.sh != startsh.sh) {
        // Detach the two subsegments [a,p] and [p,b] from subfaces.
        ssdissolve(startsh);
        ssdissolve(neighsh);
        // Create a degenerated subface [a,b,p]. It is used to: (1) hold the
        //   new segment [a,b]; (2) connect to the two adjacent subfaces
        //   [p,b,#] and [a,p,#].
        makeshellface(subfaces, &fakesh);
        setshvertices(fakesh, pa, pb, delpt);
        setshellmark(fakesh, shellmark(startsh));
        // Connect fakesh to the segment [a,b].
        ssbond(fakesh, abseg);
        // Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2].
        senext(fakesh, nextsh);
        sbond(nextsh, startsh);
        senext2(fakesh, nextsh);
        sbond(nextsh, neighsh);
        smarktest(fakesh); // Mark it as faked.
      } else {
        // Special case. There exists already a degenerated face [a,b,p]!
        //   There is no need to create a faked subface here.
        senext2self(neighsh); // [a,b,p]
        assert(sapex(neighsh) == delpt);
        // Since we will re-connect the face ring using the faked subfaces.
        //   We put the adjacent face of [a,b,p] to the list.
        spivot(neighsh, startsh); // The original adjacent subface.
        if (sorg(startsh) != pa) sesymself(startsh);
        sdissolve(startsh);
        // Connect fakesh to the segment [a,b].
        ssbond(startsh, abseg);
        fakesh = startsh; // Do not mark it!
        // Delete the degenerated subface.
        shellfacedealloc(subfaces, neighsh.sh);
      }
      // Save the fakesh in list (for re-creating the face ring).
      cavesegshlist->newindex((void **) &parysh);
      *parysh = fakesh;
    } // i
    caveshlist->restart();

    // Re-create the face ring.
    if (cavesegshlist->objects > 1) {
      for (i = 0; i < cavesegshlist->objects; i++) {
        parysh = (face *) fastlookup(cavesegshlist, i);
        fakesh = *parysh;
        // Get the next face in the ring.
        j = (i + 1) % cavesegshlist->objects;
        parysh = (face *) fastlookup(cavesegshlist, j);
        nextsh = *parysh;
        sbond1(fakesh, nextsh);
      }
    }

    // Delete the two subsegments containing p.
    shellfacedealloc(subsegs, parentseg->sh);
    shellfacedealloc(subsegs, prevseg.sh);
    // Return the new segment.
    *parentseg = abseg;
  } else {
    // p is inside the surface.
    if (b->verbose > 2) {
      printf("      Remove vertex %d from surface.\n", pointmark(delpt));
    }
    assert(sorg(*parentsh) == delpt);
    // Let 'delpt' be its apex.
    senextself(*parentsh);
    // For unifying the code, we add parentsh to list.
    cavesegshlist->newindex((void **) &parysh);
    *parysh = *parentsh;
  }

  // Remove the point (p).

  for (it = 0; it < cavesegshlist->objects; it++) {
    parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p]
    senextself(*parentsh); // [b,p,a].
    spivotself(*parentsh);
    if (sorg(*parentsh) != delpt) sesymself(*parentsh);
    // now parentsh is [p,b,#].
    if (sorg(*parentsh) != delpt) {
      // The vertex has already been removed in above special case.
      assert(!smarktested(*parentsh));
      continue;
    }

    while (1) {      
      // Initialize the flip edge list. Re-use 'caveshlist'.
      spinsh = *parentsh; // [p, b, #]
      while (1) {
        caveshlist->newindex((void **) &parysh);
        *parysh = spinsh;
        senext2self(spinsh);
        spivotself(spinsh);
        assert(spinsh.sh != nullptr);
        if (spinsh.sh == parentsh->sh) break;
        if (sorg(spinsh) != delpt) sesymself(spinsh);
        assert(sorg(spinsh) == delpt);
      } // while (1)

      if (caveshlist->objects == 3) {
        // Delete the point by a 3-to-1 flip.
        for (i = 0; i < 3; i++) {
          parysh = (face *) fastlookup(caveshlist, i);
          flipfaces[i] = *parysh;
        }
        flip31(flipfaces, lawson);
        for (i = 0; i < 3; i++) { 
          shellfacedealloc(subfaces, flipfaces[i].sh);
        }
        caveshlist->restart();
        // Save the new subface.
        caveshbdlist->newindex((void **) &parysh);
        *parysh = flipfaces[3];
        // The vertex is removed.
        break;
      }

      // Search an edge to flip.
      for (i = 0; i < caveshlist->objects; i++) {
        parysh = (face *) fastlookup(caveshlist, i);
        flipfaces[0] = *parysh;
        spivot(flipfaces[0], flipfaces[1]);
        if (sorg(flipfaces[0]) != sdest(flipfaces[1])) 
          sesymself(flipfaces[1]);
        // Skip this edge if it belongs to a faked subface.
        if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) {
          pa = sorg(flipfaces[0]);
          pb = sdest(flipfaces[0]);
          pc = sapex(flipfaces[0]);
          pd = sapex(flipfaces[1]);
          calculateabovepoint4(pa, pb, pc, pd);
          // Check if a 2-to-2 flip is possible.
          ori1 = orient3d(pc, pd, dummypoint, pa);
          ori2 = orient3d(pc, pd, dummypoint, pb);
          if (ori1 * ori2 < 0) {
            // A 2-to-2 flip is found.
            flip22(flipfaces, lawson, 0);
            // The i-th edge is flipped. The i-th and (i-1)-th subfaces are
            //   changed. The 'flipfaces[1]' contains p as its apex.
            senext2(flipfaces[1], *parentsh);
            // Save the new subface.
            caveshbdlist->newindex((void **) &parysh);
            *parysh = flipfaces[0];
            break;
          }
        } //
      } // i

      if (i == caveshlist->objects) {
        // This can happen only if there are 4 edges at p, and they are
        //   orthogonal to each other, see Fig. 2010-11-01.
        assert(caveshlist->objects == 4);
        // Do a flip22 and a flip31 to remove p.
        parysh = (face *) fastlookup(caveshlist, 0);
        flipfaces[0] = *parysh;
        spivot(flipfaces[0], flipfaces[1]);
        if (sorg(flipfaces[0]) != sdest(flipfaces[1])) {
          sesymself(flipfaces[1]);
        }
        flip22(flipfaces, lawson, 0);
        senext2(flipfaces[1], *parentsh);
        // Save the new subface.
        caveshbdlist->newindex((void **) &parysh);
        *parysh = flipfaces[0];
      }

      // The edge list at p are changed.
      caveshlist->restart();
    } // while (1)

  } // it

  cavesegshlist->restart();

  if (b->verbose > 2) {
    printf("      Created %ld new subfaces.\n", caveshbdlist->objects);
  }


  if (lawson) {
    lawsonflip();
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// slocate()    Locate a point in a surface triangulation.                   //
//                                                                           //
// Staring the search from 'searchsh'(it should not be nullptr). Perform a line //
// walk search for a subface containing the point (p).                       //
//                                                                           //
// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies     //
// above the 'searchsh' in its current orientation. The test if c is CCW to  //
// the line a->b can be done by the test if c is below the oriented plane    //
// a->b->dummypoint.                                                         //
//                                                                           //
// If 'cflag' is not TRUE, the triangulation may not be convex.  Stop search //
// when a segment is met and return OUTSIDE.                                 //
//                                                                           //
// If 'rflag' (rounding) is set, after the location of the point is found,   //
// either ONEDGE or ONFACE, round the result using an epsilon.               //
//                                                                           //
// The returned value inducates the following cases:                         //
//   - ONVERTEX, p is the origin of 'searchsh'.                              //
//   - ONEDGE, p lies on the edge of 'searchsh'.                             //
//   - ONFACE, p lies in the interior of 'searchsh'.                         //
//   - OUTSIDE, p lies outside of the triangulation, p is on the left-hand   //
//     side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, 
  face* searchsh, int aflag, int cflag, int rflag)
{
  face neighsh;
  point pa, pb, pc;
  enum locateresult loc;
  enum {MOVE_BC, MOVE_CA} nextmove;
  REAL ori, ori_bc, ori_ca;
  int i;

  pa = sorg(*searchsh);
  pb = sdest(*searchsh);
  pc = sapex(*searchsh);

  if (!aflag) {
    // No above point is given. Calculate an above point for this facet.
    calculateabovepoint4(pa, pb, pc, searchpt);
  }

  // 'dummypoint' is given. Make sure it is above [a,b,c]
  ori = orient3d(pa, pb, pc, dummypoint);
  assert(ori != 0); // SELF_CHECK
  if (ori > 0) {
    sesymself(*searchsh); // Reverse the face orientation.
  }

  // Find an edge of the face s.t. p lies on its right-hand side (CCW).
  for (i = 0; i < 3; i++) {
    pa = sorg(*searchsh);
    pb = sdest(*searchsh);
    ori = orient3d(pa, pb, dummypoint, searchpt);
    if (ori > 0) break;
    senextself(*searchsh);
  }
  assert(i < 3); // SELF_CHECK

  pc = sapex(*searchsh);

  if (pc == searchpt) {
    senext2self(*searchsh);
    return ONVERTEX;
  }

  while (1) {

    ori_bc = orient3d(pb, pc, dummypoint, searchpt);
    ori_ca = orient3d(pc, pa, dummypoint, searchpt);

    if (ori_bc < 0) {
      if (ori_ca < 0) { // (--)
        // Any of the edges is a viable move.
        if (randomnation(2)) {
          nextmove = MOVE_CA;
        } else {
          nextmove = MOVE_BC;
        }
      } else { // (-#)
        // Edge [b, c] is viable.
        nextmove = MOVE_BC;
      }
    } else {
      if (ori_ca < 0) { // (#-)
        // Edge [c, a] is viable.
        nextmove = MOVE_CA;
      } else {
        if (ori_bc > 0) {
          if (ori_ca > 0) { // (++)
            loc = ONFACE;  // Inside [a, b, c].
            break;
          } else { // (+0)
            senext2self(*searchsh); // On edge [c, a].
            loc = ONEDGE;
            break;
          }
        } else { // ori_bc == 0
          if (ori_ca > 0) { // (0+)
            senextself(*searchsh); // On edge [b, c].
            loc = ONEDGE;
            break;
          } else { // (00)
            // p is coincident with vertex c. 
            senext2self(*searchsh);
            return ONVERTEX;
          }
        }
      }
    }

    // Move to the next face.
    if (nextmove == MOVE_BC) {
      senextself(*searchsh);
    } else {
      senext2self(*searchsh);
    }
    if (!cflag) {
      // NON-convex case. Check if we will cross a boundary.
      if (isshsubseg(*searchsh)) {
        return ENCSEGMENT;
      }
    }
    spivot(*searchsh, neighsh);
    if (neighsh.sh == nullptr) {
      return OUTSIDE; // A hull edge.
    }
    // Adjust the edge orientation.
    if (sorg(neighsh) != sdest(*searchsh)) {
      sesymself(neighsh);
    }
    assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK

    // Update the newly discovered face and its endpoints.
    *searchsh = neighsh;
    pa = sorg(*searchsh);
    pb = sdest(*searchsh);
    pc = sapex(*searchsh);

    if (pc == searchpt) {
      senext2self(*searchsh);
      return ONVERTEX;
    }

  } // while (1)

  // assert(loc == ONFACE || loc == ONEDGE);


  if (rflag) {
    // Round the locate result before return.
    REAL n[3], area_abc, area_abp, area_bcp, area_cap;

    pa = sorg(*searchsh);
    pb = sdest(*searchsh);
    pc = sapex(*searchsh);

    facenormal(pa, pb, pc, n, 1, nullptr);
    area_abc = sqrt(dot(n, n));

    facenormal(pb, pc, searchpt, n, 1, nullptr);
    area_bcp = sqrt(dot(n, n));
    if ((area_bcp / area_abc) < b->epsilon) {
      area_bcp = 0; // Rounding.
    }

    facenormal(pc, pa, searchpt, n, 1, nullptr);
    area_cap = sqrt(dot(n, n));
    if ((area_cap / area_abc) < b->epsilon) {
      area_cap = 0; // Rounding
    }

    if ((loc == ONFACE) || (loc == OUTSIDE)) {
      facenormal(pa, pb, searchpt, n, 1, nullptr);
      area_abp = sqrt(dot(n, n));
      if ((area_abp / area_abc) < b->epsilon) {
        area_abp = 0; // Rounding
      }
    } else { // loc == ONEDGE
      area_abp = 0;
    }

    if (area_abp == 0) {
      if (area_bcp == 0) {
        assert(area_cap != 0);
        senextself(*searchsh); 
        loc = ONVERTEX; // p is close to b.
      } else {
        if (area_cap == 0) {
          loc = ONVERTEX; // p is close to a.
        } else {
          loc = ONEDGE; // p is on edge [a,b].
        }
      }
    } else if (area_bcp == 0) {
      if (area_cap == 0) {
        senext2self(*searchsh); 
        loc = ONVERTEX; // p is close to c.
      } else {
        senextself(*searchsh);
        loc = ONEDGE; // p is on edge [b,c].
      }
    } else if (area_cap == 0) {
      senext2self(*searchsh);
      loc = ONEDGE; // p is on edge [c,a].
    } else {
      loc = ONFACE; // p is on face [a,b,c].
    }
  } // if (rflag)

  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sscoutsegment()    Look for a segment in surface triangulation.           //
//                                                                           //
// The segment is given by the origin of 'searchsh' and 'endpt'.  Assume the //
// orientation of 'searchsh' is CCW w.r.t. the above point.                  //
//                                                                           //
// If an edge in T is found matching this segment, the segment is "locaked"  //
// in T at the edge.  Otherwise, flip the first edge in T that the segment   //
// crosses. Continue the search from the flipped face.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, 
  point endpt)
{
  face flipshs[2], neighsh;
  face newseg;
  point startpt, pa, pb, pc, pd;
  enum interresult dir;
  enum {MOVE_AB, MOVE_CA} nextmove;
  REAL ori_ab, ori_ca;

  // The origin of 'searchsh' is fixed.
  startpt = sorg(*searchsh); // pa = startpt;
  nextmove = MOVE_AB; // Avoid compiler warning.

  if (b->verbose > 2) {
    printf("      Scout segment (%d, %d).\n", pointmark(startpt),
           pointmark(endpt));
  }

  // Search an edge in 'searchsh' on the path of this segment.
  while (1) {

    pb = sdest(*searchsh);
    if (pb == endpt) {
      dir = SHAREEDGE; // Found!
      break;
    }

    pc = sapex(*searchsh);
    if (pc == endpt) {
      senext2self(*searchsh);
      sesymself(*searchsh);
      dir = SHAREEDGE; // Found!
      break;
    }

    ori_ab = orient3d(startpt, pb, dummypoint, endpt);
    ori_ca = orient3d(pc, startpt, dummypoint, endpt);

    if (ori_ab < 0) {
      if (ori_ca < 0) { // (--)
        // Both sides are viable moves.
        if (randomnation(2)) {
          nextmove = MOVE_CA;
        } else {
          nextmove = MOVE_AB;
        }
      } else { // (-#)
        nextmove = MOVE_AB;
      }
    } else {
      if (ori_ca < 0) { // (#-)
        nextmove = MOVE_CA;
      } else {
        if (ori_ab > 0) {
          if (ori_ca > 0) { // (++)
            // The segment intersects with edge [b, c].
            dir = ACROSSEDGE;
            break;
          } else { // (+0)
            // The segment collinear with edge [c, a].
            senext2self(*searchsh);
            sesymself(*searchsh);
            dir = ACROSSVERT;
            break;
          }
        } else {
          if (ori_ca > 0) { // (0+)
            // The segment collinear with edge [a, b].
            dir = ACROSSVERT;
            break;
          } else { // (00)
            // startpt == endpt. Not possible.
            assert(0); // SELF_CHECK
          }
        }
      }
    }

    // Move 'searchsh' to the next face, keep the origin unchanged.
    if (nextmove == MOVE_AB) {
      spivot(*searchsh, neighsh);
      if (sorg(neighsh) != pb) sesymself(neighsh);
      senext(neighsh, *searchsh);      
    } else {
      senext2(*searchsh, neighsh);
      spivotself(neighsh);
      if (sdest(neighsh) != pc) sesymself(neighsh);
      *searchsh = neighsh;
    }
    assert(sorg(*searchsh) == startpt); // SELF_CHECK

  } // while

  if (dir == SHAREEDGE) {
    // Insert the segment into the triangulation.
    makeshellface(subsegs, &newseg);
    setshvertices(newseg, startpt, endpt, nullptr);
    // Set the default segment marker.
    setshellmark(newseg, 1);
    ssbond(*searchsh, newseg);
    spivot(*searchsh, neighsh);
    if (neighsh.sh != nullptr) {
      ssbond(neighsh, newseg);
    }
    return dir;
  }

  if (dir == ACROSSVERT) {
    // A point is found collinear with this segment.
    return dir;
  }

  if (dir == ACROSSEDGE) {
    // Edge [b, c] intersects with the segment.
    senext(*searchsh, flipshs[0]);
    if (isshsubseg(flipshs[0])) {
      printf("Error:  Invalid PLC.\n");
      pb = sorg(flipshs[0]);
      pc = sdest(flipshs[0]);
      printf("  Two segments (%d, %d) and (%d, %d) intersect.\n",
        pointmark(startpt), pointmark(endpt), pointmark(pb), pointmark(pc));
      terminatetetgen(3);
    }
    // Flip edge [b, c], queue unflipped edges (for Delaunay checks).
    spivot(flipshs[0], flipshs[1]);
    assert(flipshs[1].sh != nullptr); // SELF_CHECK
    if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]);
    flip22(flipshs, 1, 0);
    // The flip may create an invered triangle, check it.
    pa = sapex(flipshs[1]);
    pb = sapex(flipshs[0]);
    pc = sorg(flipshs[0]);
    pd = sdest(flipshs[0]);
    // Check if pa and pb are on the different sides of [pc, pd]. 
    // Re-use ori_ab, ori_ca for the tests.
    ori_ab = orient3d(pc, pd, dummypoint, pb);
    ori_ca = orient3d(pd, pc, dummypoint, pa);
    //assert(ori_ab * ori_ca != 0); // SELF_CHECK
    if (ori_ab < 0) {
      flipshpush(&(flipshs[0]));  // push it to 'flipstack'
    } else if (ori_ca < 0) {
      flipshpush(&(flipshs[1])); // // push it to 'flipstack'
    }
    // Set 'searchsh' s.t. its origin is 'startpt'.
    *searchsh = flipshs[0];
    assert(sorg(*searchsh) == startpt);
  }

  return sscoutsegment(searchsh, endpt);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scarveholes()    Remove triangles not in the facet.                       //
//                                                                           //
// This routine re-uses the two global arrays: caveshlist and caveshbdlist.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::scarveholes(int holes, REAL* holelist)
{
  face *parysh, searchsh, neighsh;
  enum locateresult loc;
  int i, j;

  // Get all triangles. Infect unprotected convex hull triangles. 
  smarktest(recentsh);
  caveshlist->newindex((void **) &parysh);
  *parysh = recentsh;
  for (i = 0; i < caveshlist->objects; i++) {
    parysh = (face *) fastlookup(caveshlist, i);
    searchsh = *parysh;
    searchsh.shver = 0;
    for (j = 0; j < 3; j++) {
      spivot(searchsh, neighsh);
      // Is this side on the convex hull?
      if (neighsh.sh != nullptr) {
        if (!smarktested(neighsh)) {
          smarktest(neighsh);
          caveshlist->newindex((void **) &parysh);
          *parysh = neighsh;
        }
      } else {
        // A hull side. Check if it is protected by a segment.
        if (!isshsubseg(searchsh)) {
          // Not protected. Save this face.
          if (!sinfected(searchsh)) {
            sinfect(searchsh);
            caveshbdlist->newindex((void **) &parysh);
            *parysh = searchsh;
          }
        }
      }
      senextself(searchsh);
    }
  }

  // Infect the triangles in the holes.
  for (i = 0; i < 3 * holes; i += 3) {
    searchsh = recentsh;
    loc = slocate(&(holelist[i]), &searchsh, 1, 1, 0);
    if (loc != OUTSIDE) {
      sinfect(searchsh);
      caveshbdlist->newindex((void **) &parysh);
      *parysh = searchsh;
    }
  }

  // Find and infect all exterior triangles.
  for (i = 0; i < caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    searchsh = *parysh;
    searchsh.shver = 0;
    for (j = 0; j < 3; j++) {
      spivot(searchsh, neighsh);
      if (neighsh.sh != nullptr) {
        if (!isshsubseg(searchsh)) {
          if (!sinfected(neighsh)) {
            sinfect(neighsh);
            caveshbdlist->newindex((void **) &parysh);
            *parysh = neighsh;
          }
        } else {
          sdissolve(neighsh); // Disconnect a protected face.
        }
      }
      senextself(searchsh);
    }
  }

  // Delete exterior triangles, unmark interior triangles.
  for (i = 0; i < caveshlist->objects; i++) {
    parysh = (face *) fastlookup(caveshlist, i);
    if (sinfected(*parysh)) {
      shellfacedealloc(subfaces, parysh->sh);
    } else {
      sunmarktest(*parysh);
    }
  }

  caveshlist->restart();
  caveshbdlist->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// triangulate()    Create a CDT for the facet.                              //
//                                                                           //
// All vertices of the triangulation have type FACETVERTEX.  The actual type //
// of boundary vertices are set by the routine unifysements().               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist,
                             int holes, REAL* holelist)
{
  face searchsh, newsh, *parysh; 
  face newseg;
  point pa, pb, pc, *ppt, *cons;
  int iloc;
  int i, j;

  if (b->verbose > 2) {
    printf("      f%d:  %ld vertices, %ld segments", shmark, ptlist->objects,
           conlist->objects);
    if (holes > 0) {
      printf(", %d holes", holes);
    }
    printf(".\n");
  }

  if (ptlist->objects < 2l) {
    // Not a segment or a facet.
    return;
  }

  if (ptlist->objects == 2l) {
    pa = * (point *) fastlookup(ptlist, 0);
    pb = * (point *) fastlookup(ptlist, 1);
    if (distance(pa, pb) > 0) {
      // It is a single segment.
      makeshellface(subsegs, &newseg);
      setshvertices(newseg, pa, pb, nullptr);
      // Set the default segment marker '1'.
      setshellmark(newseg, 1);
    }
    if (pointtype(pa) == VOLVERTEX) {
      setpointtype(pa, FACETVERTEX);
    }
    if (pointtype(pb) == VOLVERTEX) {
      setpointtype(pb, FACETVERTEX);
    }
    return;
  } 


  // Calulcate an above point of this facet.
  if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) {
    return; // The point set is degenerate.
  }

  // Create an initial triangulation.
  makeshellface(subfaces, &newsh);
  setshvertices(newsh, pa, pb, pc);
  setshellmark(newsh, shmark);
  recentsh = newsh;

  if (pointtype(pa) == VOLVERTEX) {
    setpointtype(pa, FACETVERTEX);
  }
  if (pointtype(pb) == VOLVERTEX) {
    setpointtype(pb, FACETVERTEX);
  }
  if (pointtype(pc) == VOLVERTEX) {
    setpointtype(pc, FACETVERTEX);
  }

  // Are there area constraints?
  if (b->quality && (in->facetconstraintlist != (REAL *) nullptr)) {
    int idx, fmarker;
    REAL area;
    idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker.
    for (i = 0; i < in->numberoffacetconstraints; i++) {
      fmarker = (int) in->facetconstraintlist[i * 2];
      if (fmarker == idx) {
        area = in->facetconstraintlist[i * 2 + 1];
        setareabound(newsh, area);
        break;
      }
    }
  }

  if (ptlist->objects == 3) {
    // The triangulation only has one element.
    for (i = 0; i < 3; i++) {
      makeshellface(subsegs, &newseg);
      setshvertices(newseg, sorg(newsh), sdest(newsh), nullptr);
      // Set the default segment marker '1'.
      setshellmark(newseg, 1);
      ssbond(newsh, newseg);
      senextself(newsh);
    }
    return;
  }

  // Incrementally build the triangulation.
  pinfect(pa);
  pinfect(pb);
  pinfect(pc);
  for (i = 0; i < ptlist->objects; i++) {
    ppt = (point *) fastlookup(ptlist, i);
    if (!pinfected(*ppt)) {
      searchsh = recentsh; // Start from 'recentsh'.
      iloc = (int) OUTSIDE;
      // Insert the vertex. Use Bowyer-Watson algo. Round the location.
      iloc = sinsertvertex(*ppt, &searchsh, nullptr, iloc, 1, 1);
      if (pointtype(*ppt) == VOLVERTEX) {
        setpointtype(*ppt, FACETVERTEX);
      }
      // Delete all removed subfaces.
      for (j = 0; j < caveshlist->objects; j++) {
        parysh = (face *) fastlookup(caveshlist, j);
        shellfacedealloc(subfaces, parysh->sh);
      }
      // Clear the global lists.
      caveshbdlist->restart();
      caveshlist->restart();
      cavesegshlist->restart();
    } else {
      puninfect(*ppt); // This point has inserted.
    }
  }

  // Insert the segments.
  for (i = 0; i < conlist->objects; i++) {
    cons = (point *) fastlookup(conlist, i);
    searchsh = recentsh;
    iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0);
    if (iloc != (enum locateresult) ONVERTEX) {
      // Not found due to roundoff errors. Do a brute-force search.
      subfaces->traversalinit();
      searchsh.sh = shellfacetraverse(subfaces);
      while (searchsh.sh != nullptr) {
        // Only search the subface in the same facet.
        if (shellmark(searchsh) == shmark) {
          if ((point) searchsh.sh[3] == cons[0]) {
            searchsh.shver = 0; break;
          } else if ((point) searchsh.sh[4] == cons[0]) {
            searchsh.shver = 2; break;
          } else if ((point) searchsh.sh[5] == cons[0]) {
            searchsh.shver = 4; break;
          }
        }
        searchsh.sh = shellfacetraverse(subfaces);
          }
      assert(searchsh.sh != nullptr);
    }
    // Recover the segment. Some edges may be flipped.
    sscoutsegment(&searchsh, cons[1]);
    if (flipstack != nullptr) {
      // Recover locally Delaunay edges.
      lawsonflip();
    }
  }

  // Remove exterior and hole triangles.
  scarveholes(holes, holelist);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifysubfaces()    Unify two identical subfaces.                          //
//                                                                           //
// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b].  //
// If c = d, then f1 and f2 are identical. Otherwise, these two subfaces     //
// intersect, and the mesher is stopped.                                     //
//                                                                           //
// If the two subfaces are indentical, we try to replace f2 by f1, i.e, all  //
// neighbors of f2 are re-connected to f1.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unifysubfaces(face *f1, face *f2)
{
  if (b->psc) {
    // In this case, it is possible that two subfaces are identical.
    // While they must belong to two differnet surfaces.
    return;
  }

  point pa, pb, pc, pd;

  pa = sorg(*f1);
  pb = sdest(*f1);
  pc = sapex(*f1);
  pd = sapex(*f2);

  if (pc != pd) {
    printf("Found two facets intersect each other.\n");
    printf("  1st: [%d, %d, %d] #%d\n", 
               pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
    printf("  2nd: [%d, %d, %d] #%d\n",
               pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
    // [Bruno]
    // Keep track of the facets that intersect.
    in->isectfaces.insert(shellmark(*f1));
    in->isectfaces.insert(shellmark(*f2));
    terminatetetgen(3);
  } else {
    printf("Found two duplicated facets.\n");
    printf("  1st: [%d, %d, %d] #%d\n", 
               pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
    printf("  2nd: [%d, %d, %d] #%d\n",
               pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
    // [Bruno]
    // Keep track of the facets that intersect.
    in->isectfaces.insert(shellmark(*f1));
    in->isectfaces.insert(shellmark(*f2));
    terminatetetgen(3);
  }

}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifysegments()    Remove redundant segments and create face links.       //
//                                                                           //
// After this routine, although segments are unique, but some of them may be //
// removed later by mergefacet().  All vertices still have type FACETVERTEX. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unifysegments()
{
  badface *facelink = nullptr, *newlinkitem, *f1, *f2;
  face *facperverlist, sface;
  face subsegloop, testseg;
  point torg, tdest;
  REAL ori1, ori2, ori3;
  REAL n1[3], n2[3];
  int *idx2faclist;
  int idx, k, m;

  if (b->verbose > 1) {
    printf("  Unifying segments.\n");
  }

  // Create a mapping from vertices to subfaces.
  makepoint2submap(subfaces, idx2faclist, facperverlist);

  if (b->psc) {
    face sface1;
    face seg, seg1;
    int fmarker, fmarker1;
    // First only connect subfaces which belong to the same surfaces.
    subsegloop.shver = 0;
    subsegs->traversalinit();
    subsegloop.sh = shellfacetraverse(subsegs);
    while (subsegloop.sh != (shellface *) nullptr) {
      torg = sorg(subsegloop);
      tdest = sdest(subsegloop);

      idx = pointmark(torg) - in->firstnumber;
      for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) {
        sface = facperverlist[k];
        // The face may be deleted if it is a duplicated face.
        if (sface.sh[3] == nullptr) continue;
        // Search the edge torg->tdest.
        assert(sorg(sface) == torg); // SELF_CHECK
        if (sdest(sface) != tdest) {
          senext2self(sface);
          sesymself(sface);
        }
        if (sdest(sface) != tdest) continue;

        sspivot(sface, seg);
        if (seg.sh == nullptr) continue;
        // assert(seg.sh != nullptr); It may or may not be subsegloop.

        // Find the adjacent subface on the same facet.
        fmarker = in->facetmarkerlist[shellmark(sface) - 1];
        sface1.sh = nullptr;
        k++;
        for (; k < idx2faclist[idx + 1]; k++) {
          sface1 = facperverlist[k];
          // The face may be deleted if it is a duplicated face.
          if (sface1.sh[3] == nullptr) continue;
          // Search the edge torg->tdest.
          assert(sorg(sface1) == torg); // SELF_CHECK
          if (sdest(sface1) != tdest) {
            senext2self(sface1);
            sesymself(sface1);
          }
          if (sdest(sface1) != tdest) continue;
          // Found a subface sharing at the same edge.
          fmarker1 = in->facetmarkerlist[shellmark(sface1) - 1];
          if (fmarker1 == fmarker) {
            // Found a pair of adjacent subfaces. Connect them.
            // Delete a redundent segment.
            sspivot(sface1, seg1); 
            assert(seg1.sh != nullptr); // SELF_CHECK
            shellfacedealloc(subsegs, seg.sh);
            shellfacedealloc(subsegs, seg1.sh);
            ssdissolve(sface);
            ssdissolve(sface1);
            // Connect them.
            sbond(sface, sface1);
            // Set Steiner point -to- subface map.
            if (pointtype(torg) == FREEFACETVERTEX) {
              setpoint2sh(torg, sencode(sface));
            }
            if (pointtype(tdest) == FREEFACETVERTEX) {
              setpoint2sh(tdest, sencode(sface));
            }
            break;
          }
        }
        break;
      }
      subsegloop.sh = shellfacetraverse(subsegs);
    }
  } // if (b->psc)

  subsegloop.shver = 0;
  subsegs->traversalinit();
  subsegloop.sh = shellfacetraverse(subsegs);
  while (subsegloop.sh != (shellface *) nullptr) {
    torg = sorg(subsegloop);
    tdest = sdest(subsegloop);

    idx = pointmark(torg) - in->firstnumber;
    // Loop through the set of subfaces containing 'torg'.  Get all the
    //   subfaces containing the edge (torg, tdest). Save and order them
    //   in 'sfacelist', the ordering is defined by the right-hand rule
    //   with thumb points from torg to tdest.
    for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) {
      sface = facperverlist[k];
      // The face may be deleted if it is a duplicated face.
      if (sface.sh[3] == nullptr) continue;
      // Search the edge torg->tdest.
      assert(sorg(sface) == torg); // SELF_CHECK
      if (sdest(sface) != tdest) {
        senext2self(sface);
        sesymself(sface);
      }
      if (sdest(sface) != tdest) continue;

      // Save the face f in facelink.
      if (flippool->items >= 2) {
        f1 = facelink;
        for (m = 0; m < flippool->items - 1; m++) {
          f2 = f1->nextitem;
          ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss));
          ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
          if (ori1 > 0) {
            // apex(f2) is below f1.
            if (ori2 > 0) {
              // apex(f) is below f1 (see Fig.1). 
              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
              if (ori3 > 0) {
                // apex(f) is below f2, insert it.
                break; 
              } else if (ori3 < 0) {
                // apex(f) is above f2, continue.
              } else { // ori3 == 0; 
                // f is coplanar and codirection with f2.
                unifysubfaces(&(f2->ss), &sface);
                break;
              }
            } else if (ori2 < 0) {
              // apex(f) is above f1 below f2, inset it (see Fig. 2).
              break;
            } else { // ori2 == 0;
              // apex(f) is coplanar with f1 (see Fig. 5).
              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
              if (ori3 > 0) {
                // apex(f) is below f2, insert it.
                break; 
              } else {
                // f is coplanar and codirection with f1.
                unifysubfaces(&(f1->ss), &sface);
                break;
              }
            }
          } else if (ori1 < 0) {
            // apex(f2) is above f1.
            if (ori2 > 0) {
              // apex(f) is below f1, continue (see Fig. 3).
            } else if (ori2 < 0) {
              // apex(f) is above f1 (see Fig.4).
              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
              if (ori3 > 0) {
                // apex(f) is below f2, insert it.
                break;
              } else if (ori3 < 0) {
                // apex(f) is above f2, continue.
              } else { // ori3 == 0;
                // f is coplanar and codirection with f2.
                unifysubfaces(&(f2->ss), &sface);
                break;
              }
            } else { // ori2 == 0;
              // f is coplanar and with f1 (see Fig. 6).
              ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface));
              if (ori3 > 0) {
                // f is also codirection with f1.
                unifysubfaces(&(f1->ss), &sface);
                break;
              } else {
                // f is above f2, continue.
              }
            }
          } else { // ori1 == 0;
            // apex(f2) is coplanar with f1. By assumption, f1 is not
            //   coplanar and codirection with f2.
            if (ori2 > 0) {
              // apex(f) is below f1, continue (see Fig. 7).
            } else if (ori2 < 0) {
              // apex(f) is above f1, insert it (see Fig. 7).
              break;
            } else { // ori2 == 0.
              // apex(f) is coplanar with f1 (see Fig. 8).
              // f is either codirection with f1 or is codirection with f2. 
              facenormal(torg, tdest, sapex(f1->ss), n1, 1, nullptr);
              facenormal(torg, tdest, sapex(sface), n2, 1, nullptr);
              if (dot(n1, n2) > 0) {
                unifysubfaces(&(f1->ss), &sface);
              } else {
                unifysubfaces(&(f2->ss), &sface);
              }
              break;
            }
          }
          // Go to the next item;
          f1 = f2;
        } // for (m = 0; ...)
        if (sface.sh[3] != nullptr) {
          // Insert sface between f1 and f2.
          newlinkitem = (badface *) flippool->alloc();
          newlinkitem->ss = sface;
          newlinkitem->nextitem = f1->nextitem;
          f1->nextitem = newlinkitem;
        }
      } else if (flippool->items == 1) {
        f1 = facelink;
        // Make sure that f is not coplanar and codirection with f1.
        ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface));
        if (ori1 == 0) {
          // f is coplanar with f1 (see Fig. 8).
          facenormal(torg, tdest, sapex(f1->ss), n1, 1, nullptr);
          facenormal(torg, tdest, sapex(sface), n2, 1, nullptr);
          if (dot(n1, n2) > 0) {
            // The two faces are codirectional as well.
            unifysubfaces(&(f1->ss), &sface);
          }
        }
        // Add this face to link if it is not deleted.
        if (sface.sh[3] != nullptr) {
          // Add this face into link.
          newlinkitem = (badface *) flippool->alloc();
          newlinkitem->ss = sface;
          newlinkitem->nextitem = nullptr;
          f1->nextitem = newlinkitem;
        }
      } else {
        // The first face.
        newlinkitem = (badface *) flippool->alloc();
        newlinkitem->ss = sface;
        newlinkitem->nextitem = nullptr;
        facelink = newlinkitem;
      }
    } // for (k = idx2faclist[idx]; ...)

    if (b->psc) {
      // Set Steiner point -to- segment map.
      if (pointtype(torg) == FREESEGVERTEX) {
        setpoint2sh(torg, sencode(subsegloop));
      }
      if (pointtype(tdest) == FREESEGVERTEX) {
        setpoint2sh(tdest, sencode(subsegloop));
      }
    }

    // Set the connection between this segment and faces containing it,
    //   at the same time, remove redundant segments.
    f1 = facelink;
    for (k = 0; k < flippool->items; k++) {
      sspivot(f1->ss, testseg);
      // If 'testseg' is not 'subsegloop' and is not dead, it is redundant.
      if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != nullptr)) {
        shellfacedealloc(subsegs, testseg.sh);
      }
      // Bonds the subface and the segment together.
      ssbond(f1->ss, subsegloop);
      f1 = f1->nextitem;
    }

    // Create the face ring at the segment.
    if (flippool->items > 1) {
      f1 = facelink;
      for (k = 1; k <= flippool->items; k++) {
        k < flippool->items ? f2 = f1->nextitem : f2 = facelink;
        sbond1(f1->ss, f2->ss);
        f1 = f2;
      }
    }

    // All identified segments has an init marker "0".
    flippool->restart();

    // Are there length constraints?
    if (b->quality && (in->segmentconstraintlist != (REAL *) nullptr)) {
      int e1, e2;
      REAL len;
      for (k = 0; k < in->numberofsegmentconstraints; k++) {
        e1 = (int) in->segmentconstraintlist[k * 3];
        e2 = (int) in->segmentconstraintlist[k * 3 + 1];
        if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) ||
            ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) {
          len = in->segmentconstraintlist[k * 3 + 2];
          setareabound(subsegloop, len);
          break;
        }
      }
    }

    subsegloop.sh = shellfacetraverse(subsegs);
  }

  delete [] idx2faclist;
  delete [] facperverlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// mergefacets()    Merge adjacent facets.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::mergefacets()
{
  face parentsh, neighsh, neineish;
  face segloop;
  point pa, pb, pc, pd;
  REAL ang_tol, ang;
  int remsegcount;
  int fidx1, fidx2;
  int fmrk1, fmrk2;

  if (b->verbose > 1) {
    printf("    Merging adjacent facets.\n");
  }

  // The dihedral angle bound for two different facets.
  //   Set by -p option. Default is 179 degree.
  ang_tol = b->facet_ang_tol / 180.0 * PI;
  remsegcount = 0;

  // Loop all segments, merge adjacent coplanar facets.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) nullptr) {
    spivot(segloop, parentsh);
    if (parentsh.sh != nullptr) {
      spivot(parentsh, neighsh);
      if (neighsh.sh != nullptr) {
        spivot(neighsh, neineish);
        if (neineish.sh == parentsh.sh) {
          // Exactly two subfaces at this segment.
          fidx1 = shellmark(parentsh) - 1;
          fidx2 = shellmark(neighsh) - 1;
          // Only merge them if they are in different facet.
          if (fidx1 != fidx2) {
            // The two subfaces are not in the same facet.
            if (in->facetmarkerlist != nullptr) { 
              fmrk1 = in->facetmarkerlist[fidx1];
              fmrk2 = in->facetmarkerlist[fidx2];
            } else {
              fmrk1 = fmrk2 = 0;
            }
            // Only merge them if they have the same boundary marker.
            if (fmrk1 == fmrk2) {
              pa = sorg(segloop);
              pb = sdest(segloop);
              pc = sapex(parentsh);
              pd = sapex(neighsh);
              // Calculate the dihedral angle at the segment [a,b].
              ang = facedihedral(pa, pb, pc, pd);
              if (ang > PI) ang = (2 * PI - ang);
              if (ang > ang_tol) {
                remsegcount++;
                ssdissolve(parentsh);
                ssdissolve(neighsh);
                shellfacedealloc(subsegs, segloop.sh);
                // Add the edge to flip stack.
                flipshpush(&parentsh);
              } // if (ang > ang_tol)
            } // if (fmrk1 == fmrk2)
          } // if (fidx1 != fidx2)
        } // if (neineish.sh == parentsh.sh)
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  if (flipstack != nullptr) {
    lawsonflip(); // Recover Delaunayness.
  }

  if (b->verbose > 1) {
    printf("    %d segments are removed.\n", remsegcount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// identifypscedges()    Identify PSC edges.                                 //
//                                                                           //
// The set of PSC edges are provided in the 'in->edgelist'. Each edge should //
// also be an edge in the surface mesh.  We find the corresponding edges in  //
// the surface mesh and make them segments of the mesh.                      //
//                                                                           //
// It is possible to give an edge which is not in any facet, i.e., it is a   //
// dangling edge inside the volume.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::identifypscedges(point *idx2verlist)
{
  face* shperverlist;
  int* idx2shlist;
  face searchsh, neighsh;
  face segloop, checkseg, newseg;
  point checkpt, pa = nullptr, pb = nullptr;
  int *endpts;
  int edgemarker;
  int idx, i, j;

  int e1, e2;
  REAL len;

  if (!b->quiet) {
    printf("Inserting edges ...\n");
  }

  // All identified segments have the initial marker '1'.
  // All segments inserted here should have a marker 'k >= 0'.

  if (b->psc) {
    // First mark all segments of the mesh with a marker '-1'.
    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != nullptr) {
      setshellmark(segloop, -1);
      segloop.sh = shellfacetraverse(subsegs);
    }
  }

  // Construct a map from points to subfaces.
  makepoint2submap(subfaces, idx2shlist, shperverlist);

  // Process the set of PSC edges.
  for (i = 0; i < in->numberofedges; i++) {
    endpts = &(in->edgelist[(i << 1)]);
    // Find a face contains the edge.
    newseg.sh = nullptr;
    searchsh.sh = nullptr;
    idx = endpts[0] - in->firstnumber;
    for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) {
      checkpt = sdest(shperverlist[j]);
      if (pointmark(checkpt) == endpts[1]) {
        searchsh = shperverlist[j];
        break; // Found.
      } else {
        checkpt = sapex(shperverlist[j]);
        if (pointmark(checkpt) == endpts[1]) {
          senext2(shperverlist[j], searchsh);
          sesymself(searchsh);
          break;
        }
      }
    } // j
    edgemarker = 0;
    if (in->edgemarkerlist) {
      edgemarker = in->edgemarkerlist[i];
    }

    if (searchsh.sh != nullptr) {
      // Check if this edge is already a segment of the mesh.
      sspivot(searchsh, checkseg);
      if (checkseg.sh != nullptr) {
        // There should be no duplicated edges.
        setshellmark(checkseg, edgemarker);
      } else {
        // Create a new segment at this edge.
        pa = sorg(searchsh);
        pb = sdest(searchsh);
        makeshellface(subsegs, &newseg);
        setshvertices(newseg, pa, pb, nullptr);
        setshellmark(newseg, edgemarker);
        ssbond(searchsh, newseg);
        spivot(searchsh, neighsh);
        if (neighsh.sh != nullptr) {
          ssbond(neighsh, newseg);
        }
        if (b->psc) {
          if (pointtype(pa) == FREESEGVERTEX) {
            setpoint2sh(pa, sencode(newseg));
          }
          if (pointtype(pb) == FREESEGVERTEX) {
            setpoint2sh(pb, sencode(newseg));
          }
        }
      }
    } else {
      // It is a dangling segment (not belong to any facets).
      // Get the two endpoints of this segment.
      pa = idx2verlist[endpts[0]];
      pb = idx2verlist[endpts[1]];
      makeshellface(subsegs, &newseg);
      setshvertices(newseg, pa, pb, nullptr);
      setshellmark(newseg, edgemarker);
      if (b->psc) {
        if (pointtype(pa) == FREESEGVERTEX) {
          setpoint2sh(pa, sencode(newseg));
        }
        if (pointtype(pb) == FREESEGVERTEX) {
          setpoint2sh(pb, sencode(newseg));
        }
      }
    }

    if (newseg.sh != nullptr) {
      if (b->quality && (in->segmentconstraintlist != (REAL *) nullptr)) {
        for (i = 0; i < in->numberofsegmentconstraints; i++) {
          e1 = (int) in->segmentconstraintlist[i * 3];
          e2 = (int) in->segmentconstraintlist[i * 3 + 1];
          if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) ||
              ((pointmark(pa) == e2) && (pointmark(pb) == e1))) {
            len = in->segmentconstraintlist[i * 3 + 2];
            setareabound(newseg, len);
            break;
          }
        }
      }
    }
  } // i


  delete [] shperverlist;
  delete [] idx2shlist;

  if (b->psc) {
    // Connecting subsegments at Steiner points.
    face seg1, seg2;
    // Re-use 'idx2shlist' and 'shperverlist'.
    makepoint2submap(subsegs, idx2shlist, shperverlist);

    points->traversalinit();
    pa = pointtraverse();
    while (pa != nullptr) {
      if (pointtype(pa) == FREESEGVERTEX) {
        idx = pointmark(pa) - in->firstnumber;
        // There must be only two segments containing this vertex.
        assert((idx2shlist[idx + 1] - idx2shlist[idx]) == 2);
        i = idx2shlist[idx];
        seg1 = shperverlist[i];
        seg2 = shperverlist[i+1];
        senextself(seg1);
        senextself(seg2);
        sbond(seg1, seg2);
      }
      pa = pointtraverse();
    }

    delete [] shperverlist;
    delete [] idx2shlist;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// meshsurface()    Create a surface mesh of the input PLC.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::meshsurface()
{
  arraypool *ptlist, *conlist;
  point *idx2verlist;
  point tstart, tend, *pnewpt, *cons;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  int end1, end2;
  int shmark, i, j;

  if (!b->quiet) {
    printf("Creating surface mesh ...\n");
  }

  // Create a map from indices to points.
  makeindex2pointmap(idx2verlist);

  // Initialize arrays (block size: 2^8 = 256).
  ptlist = new arraypool(sizeof(point *), 8);
  conlist = new arraypool(2 * sizeof(point *), 8);

  // Loop the facet list, triangulate each facet.
  for (shmark = 1; shmark <= in->numberoffacets; shmark++) {

    // Get a facet F.
    f = &in->facetlist[shmark - 1];

    // Process the duplicated points first, they are marked with type
    //   DUPLICATEDVERTEX.  If p and q are duplicated, and p'index > q's,
    //   then p is substituted by q.
    if (dupverts > 0l) {
      // Loop all polygons of this facet.
      for (i = 0; i < f->numberofpolygons; i++) {
        p = &(f->polygonlist[i]);
        // Loop other vertices of this polygon.
        for (j = 0; j < p->numberofvertices; j++) {
          end1 = p->vertexlist[j];
          tstart = idx2verlist[end1];
          if (pointtype(tstart) == DUPLICATEDVERTEX) {
            // Reset the index of vertex-j.
            tend = point2ppt(tstart);
            end2 = pointmark(tend);
            p->vertexlist[j] = end2;
          }
        }
      }
    }

    // Loop polygons of F, get the set of vertices and segments.
    for (i = 0; i < f->numberofpolygons; i++) {
      // Get a polygon.
      p = &(f->polygonlist[i]);
      // Get the first vertex.
      end1 = p->vertexlist[0];
      if ((end1 < in->firstnumber) || 
          (end1 >= in->firstnumber + in->numberofpoints)) {
        if (!b->quiet) {
          printf("Warning:  Invalid the 1st vertex %d of polygon", end1);
          printf(" %d in facet %d.\n", i + 1, shmark);
        }
        continue; // Skip this polygon.
      }
      tstart = idx2verlist[end1];
      // Add tstart to V if it haven't been added yet.
      if (!pinfected(tstart)) {
        pinfect(tstart);
        ptlist->newindex((void **) &pnewpt);
        *pnewpt = tstart;
      }
      // Loop other vertices of this polygon.
      for (j = 1; j <= p->numberofvertices; j++) {
        // get a vertex.
        if (j < p->numberofvertices) {
          end2 = p->vertexlist[j];
        } else {
          end2 = p->vertexlist[0];  // Form a loop from last to first.
        }
        if ((end2 < in->firstnumber) ||
            (end2 >= in->firstnumber + in->numberofpoints)) {
          if (!b->quiet) {
            printf("Warning:  Invalid vertex %d in polygon %d", end2, i + 1);
            printf(" in facet %d.\n", shmark);
          }
        } else {
          if (end1 != end2) {
            // 'end1' and 'end2' form a segment.
            tend = idx2verlist[end2];
            // Add tstart to V if it haven't been added yet.
            if (!pinfected(tend)) {
              pinfect(tend);
              ptlist->newindex((void **) &pnewpt);
              *pnewpt = tend;
            }
            // Save the segment in S (conlist).
            conlist->newindex((void **) &cons);
            cons[0] = tstart;
            cons[1] = tend;
            // Set the start for next continuous segment.
            end1 = end2;
            tstart = tend;
          } else {
            // Two identical vertices mean an isolated vertex of F.
            if (p->numberofvertices > 2) {
              // This may be an error in the input, anyway, we can continue
              //   by simply skipping this segment.
              if (!b->quiet) {
                printf("Warning:  Polygon %d has two identical verts", i + 1);
                printf(" in facet %d.\n", shmark);
              }
            } 
            // Ignore this vertex.
          }
        }
        // Is the polygon degenerate (a segment or a vertex)?
        if (p->numberofvertices == 2) break;
      }
    }
    // Unmark vertices.
    for (i = 0; i < ptlist->objects; i++) {
      pnewpt = (point *) fastlookup(ptlist, i);
      puninfect(*pnewpt);
    }

    // Triangulate F into a CDT.
    triangulate(shmark, ptlist, conlist, f->numberofholes, f->holelist);

    // Clear working lists.
    ptlist->restart();
    conlist->restart();
  }

  if (!b->diagnose) {
    // Remove redundant segments and build the face links.
    unifysegments();
  }

  if (!b->psc && !b->nomergefacet && !b->nobisect && !b->diagnose) {
    // Merge adjacent coplanar facets.
    mergefacets();
  }

  if (in->numberofedges > 0) { // if (b->psc)
    // There are segments specified by the user. Read and create them.
    identifypscedges(idx2verlist);
  }

  if (b->object == tetgenbehavior::STL) {
    // Remove redundant vertices (for .stl input mesh).
    jettisonnodes();
  }

  if (b->verbose) {
    printf("  %ld (%ld) subfaces (segments).\n", subfaces->items, 
           subsegs->items);
  }

  // The total number of iunput segments.
  insegments = subsegs->items;

  delete [] idx2verlist;
  delete ptlist;
  delete conlist;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interecursive()    Recursively do intersection test on a set of triangles.//
//                                                                           //
// Recursively split the set 'subfacearray' of subfaces into two sets using  //
// a cut plane parallel to x-, or, y-, or z-axies.  The split criteria are   //
// follows. Assume the cut plane is H, and H+ denotes the left halfspace of  //
// H, and H- denotes the right halfspace of H; and s be a subface:           //
//                                                                           //
//    (1) If all points of s lie at H+, put it into left array;              //
//    (2) If all points of s lie at H-, put it into right array;             //
//    (3) If some points of s lie at H+ and some of lie at H-, or some       //
//        points lie on H, put it into both arraies.                         //
//                                                                           //
// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis  //
// if axis == '2'. If current cut plane is parallel to the x-axis, the next  //
// one will be parallel to y-axis, and the next one after the next is z-axis,//
// and then alternately return back to x-axis.                               //
//                                                                           //
// Stop splitting when the number of triangles of the input array is not     //
// decreased anymore. Do tests on the current set.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, 
                               int axis, REAL bxmin, REAL bxmax, REAL bymin, 
                               REAL bymax, REAL bzmin, REAL bzmax, 
                               int* internum)
{
  shellface **leftarray, **rightarray;
  face sface1, sface2;
  point p1, p2, p3;
  point p4, p5, p6;
  enum interresult intersect;
  REAL split;
  bool toleft, toright;
  int leftsize, rightsize;
  int i, j;

  if (b->verbose > 2) {
    printf("      Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
           arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
           axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
  }
    
  leftarray = new shellface*[arraysize];
  if (leftarray == nullptr) {
    terminatetetgen(1);
  }
  rightarray = new shellface*[arraysize];
  if (rightarray == nullptr) {
    terminatetetgen(1);
  }
  leftsize = rightsize = 0;

  if (axis == 0) {
    // Split along x-axis.
    split = 0.5 * (bxmin + bxmax);
  } else if (axis == 1) {
    // Split along y-axis.
    split = 0.5 * (bymin + bymax);
  } else {
    // Split along z-axis.
    split = 0.5 * (bzmin + bzmax);
  }

  for (i = 0; i < arraysize; i++) {
    sface1.sh = subfacearray[i];
    p1 = (point) sface1.sh[3];
    p2 = (point) sface1.sh[4];
    p3 = (point) sface1.sh[5];
    toleft = toright = false;
    if (p1[axis] < split) {
      toleft = true;
      if (p2[axis] >= split || p3[axis] >= split) {
        toright = true;
      } 
    } else if (p1[axis] > split) {
      toright = true;
      if (p2[axis] <= split || p3[axis] <= split) {
        toleft = true;
      } 
    } else {
      // p1[axis] == split;
      toleft = true;
      toright = true;
    }
    // At least one is true;
    assert(!(toleft == false && toright == false));
    if (toleft) {
      leftarray[leftsize] = sface1.sh;
      leftsize++;
    }
    if (toright) {
      rightarray[rightsize] = sface1.sh;
      rightsize++;
    }
  }

  if (leftsize < arraysize && rightsize < arraysize) {
    // Continue to partition the input set. Now 'subfacearray' has been
    //   split into two sets, it's memory can be freed. 'leftarray' and
    //   'rightarray' will be freed in the next recursive (after they're
    //   partitioned again or performing tests).
    delete [] subfacearray;
    // Continue to split these two sets.
    if (axis == 0) {
      interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax,
                    bzmin, bzmax, internum);
    } else if (axis == 1) {
      interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax,
                    bzmin, bzmax, internum);
    } else {
      interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax,
                    bzmin, split, internum);
      interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax,
                    split, bzmax, internum);
    }
  } else {
    if (b->verbose > 1) {
      printf("  Checking intersecting faces.\n");
    }
    // Perform a brute-force compare on the set.
    for (i = 0; i < arraysize; i++) {
      sface1.sh = subfacearray[i];
      p1 = (point) sface1.sh[3];
      p2 = (point) sface1.sh[4];
      p3 = (point) sface1.sh[5];
      for (j = i + 1; j < arraysize; j++) {
        sface2.sh = subfacearray[j];
        p4 = (point) sface2.sh[3];
        p5 = (point) sface2.sh[4];
        p6 = (point) sface2.sh[5];
        intersect = (enum interresult) tri_tri_inter(p1, p2, p3, p4, p5, p6);
        if (intersect == INTERSECT || intersect == SHAREFACE) {
            // [Bruno]
            // Keep track of the facets that intersect.
            in->isectfaces.insert(shellmark(sface1));
            in->isectfaces.insert(shellmark(sface2));
          if (!b->quiet) {
            if (intersect == INTERSECT) {
              printf("  Facet #%d intersects facet #%d at triangles:\n",
                     shellmark(sface1), shellmark(sface2));
              printf("    (%4d, %4d, %4d) and (%4d, %4d, %4d)\n",
                     pointmark(p1), pointmark(p2), pointmark(p3),
                     pointmark(p4), pointmark(p5), pointmark(p6));
            } else {
              printf("  Facet #%d duplicates facet #%d at triangle:\n",
                     shellmark(sface1), shellmark(sface2));
              printf("    (%4d, %4d, %4d) and (%4d, %4d, %4d)\n",
                     pointmark(p1), pointmark(p2), pointmark(p3),
                     pointmark(p4), pointmark(p5), pointmark(p6));
            }
          }
          // Increase the number of intersecting pairs.
          (*internum)++; 
          // Infect these two faces (although they may already be infected).
          sinfect(sface1);
          sinfect(sface2);
        }
      }
    }
    // Don't forget to free all three arrays. No further partition.
    delete [] leftarray;
    delete [] rightarray;  
    delete [] subfacearray;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// detectinterfaces()    Detect intersecting triangles.                      //
//                                                                           //
// Given a set of triangles,  find the pairs of intersecting triangles from  //
// them.  Here the set of triangles is in 'subfaces' which is a surface mesh //
// of a PLC (.poly or .smesh).                                               //
//                                                                           //
// To detect whether two triangles are intersecting is done by the routine   //
// 'tri_tri_inter()'.  The algorithm for the test is very simple and stable. //
// It is based on geometric orientation test which uses exact arithmetics.   //
//                                                                           //
// Use divide-and-conquer algorithm for reducing the number of intersection  //
// tests.  Start from the bounding box of the input point set, recursively   //
// partition the box into smaller boxes, until the number of triangles in a  //
// box is not decreased anymore. Then perform triangle-triangle tests on the //
// remaining set of triangles.  The memory allocated in the input set is     //
// freed immediately after it has been partitioned into two arrays.  So it   //
// can be re-used for the consequent partitions.                             //
//                                                                           //
// On return, the pool 'subfaces' will be cleared, and only the intersecting //
// triangles remain for output (to a .face file).                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::detectinterfaces()
{
  shellface **subfacearray;
  face shloop;
  int internum;
  int i;

  if (!b->quiet) {
    printf("Detecting self-intersecting facets...\n");
  }

  // Construct a map from indices to subfaces;
  subfacearray = new shellface*[subfaces->items];
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  i = 0;
  while (shloop.sh != (shellface *) nullptr) {
    subfacearray[i] = shloop.sh;
    shloop.sh = shellfacetraverse(subfaces);
    i++;
  }

  internum = 0;
  // Recursively split the set of triangles into two sets using a cut plane
  //   parallel to x-, or, y-, or z-axies.  Stop splitting when the number
  //   of subfaces is not decreasing anymore. Do tests on the current set.
  interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax,
                zmin, zmax, &internum);

  if (!b->quiet) {
    if (internum > 0) {
      printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum);
    } else {
      printf("\nNo faces are intersecting.\n\n");
    }
  }

  if (internum > 0) {
    // Traverse all subfaces, deallocate those have not been infected (they
    //   are not intersecting faces). Uninfect those have been infected.
    //   After this loop, only intersecting faces remain.
    subfaces->traversalinit();
    shloop.sh = shellfacetraverse(subfaces);
    while (shloop.sh != (shellface *) nullptr) {
      if (sinfected(shloop)) {
        suninfect(shloop);
      } else {
        shellfacedealloc(subfaces, shloop.sh);
      }
      shloop.sh = shellfacetraverse(subfaces);
    }
  } else {
    // Deallocate all subfaces.
    subfaces->restart();
  }
}

////                                                                       ////
////                                                                       ////
//// surface_cxx //////////////////////////////////////////////////////////////

//// constrained_cxx //////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// markacutevertices()    Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. //
//                                                                           //
// Initialize all segment vertices's type be RIDGEVERTEX. A segment is acute //
// if there are at least two segments incident at it form an angle less than //
// theta (= 60 degree).                                                      //
//                                                                           //
// The minimum segment-segment angle (minfaceang) is calculated.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::markacutevertices()
{
  face* segperverlist;
  int* idx2seglist;
  point pa, pb, pc;
  REAL anglimit, sharpanglimit, ang;
  bool acuteflag;
  int acutecount, sharpsegcount;
  int idx, i, j;

  if (b->verbose) {
    printf("  Marking acute vertices.\n");
  }
  anglimit = PI / 3.0;  // 60 degree.
  sharpanglimit = 5.0 / 180.0 * PI; // 5 degree. 
  minfaceang = PI; // 180 degree.
  acutecount = sharpsegcount = 0;

  // Construct a map from points to segments.
  makepoint2submap(subsegs, idx2seglist, segperverlist);

  // Loop over the set of vertices.
  points->traversalinit();
  pa = pointtraverse();
  while (pa != nullptr) {
    idx = pointmark(pa) - in->firstnumber;
    // Mark it if it is an endpoint of some segments.
    if (idx2seglist[idx + 1] > idx2seglist[idx]) {
      // It is an endpoint of some segments.
      setpointtype(pa, RIDGEVERTEX);
      acuteflag = false;
      // Do a brute-force pair-pair check.
      for (i=idx2seglist[idx]; i<idx2seglist[idx + 1]; i++) {
        pb = sdest(segperverlist[i]);
        for (j = i + 1; j < idx2seglist[idx + 1]; j++) {
          pc = sdest(segperverlist[j]);
          ang = interiorangle(pa, pb, pc, nullptr); 
          if (!acuteflag) {
            acuteflag = ang < anglimit;
          }
          // Remember the smallest angle.
          if (ang < minfaceang) minfaceang = ang;
          // Mark segments at extremely small angle.
          if (ang < sharpanglimit) {
            if (shelltype(segperverlist[i]) != SHARP) {
              setshelltype(segperverlist[i], SHARP);
              sharpsegcount++;
            }
            if (shelltype(segperverlist[j]) != SHARP) {
              setshelltype(segperverlist[j], SHARP);
              sharpsegcount++;
            }            
          }
        } // j
      } // i
      if (acuteflag) {
        setpointtype(pa, ACUTEVERTEX);
        acutecount++;
      }
    }
    pa = pointtraverse();
  }

  if (b->verbose) {
    if (acutecount > 0) {
      printf("  Found %d acute vertices.\n", acutecount);
    }
    if (sharpsegcount > 0) {
      printf("  Found %d sharp segments.\n", sharpsegcount);
    }
    printf("  Minimum seg-seg angle = %g.\n", minfaceang / PI * 180.0);
  }

  delete [] idx2seglist;
  delete [] segperverlist;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirection()    Find the tet on the path from one point to another.    //
//                                                                           //
// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, //
// 'searchtet' contains a tet on the path, its origin does not change.       //
//                                                                           //
// The return value indicates one of the following cases (let 'searchtet' be //
// abcd, a is the origin of the path):                                       //
//   - ACROSSVERT, edge ab is collinear with the path;                       //
//   - ACROSSEDGE, edge bc intersects with the path;                         //
//   - ACROSSFACE, face bcd intersects with the path.                        //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult 
  tetgenmesh::finddirection(triface* searchtet, point endpt)
{
  triface neightet;
  point pa, pb, pc, pd;
  enum {HMOVE, RMOVE, LMOVE} nextmove;
  REAL hori, rori, lori;
  int t1ver;
  int s;

  // The origin is fixed.
  pa = org(*searchtet);
  if ((point) searchtet->tet[7] == dummypoint) {
    // A hull tet. Choose the neighbor of its base face.
    decode(searchtet->tet[3], *searchtet);
    // Reset the origin to be pa.
    if ((point) searchtet->tet[4] == pa) {
      searchtet->ver = 11;
    } else if ((point) searchtet->tet[5] == pa) {
      searchtet->ver = 3;
    } else if ((point) searchtet->tet[6] == pa) {
      searchtet->ver = 7;
    } else {
      assert((point) searchtet->tet[7] == pa); 
      searchtet->ver = 0;
    }
  }

  pb = dest(*searchtet);
  // Check whether the destination or apex is 'endpt'.
  if (pb == endpt) {
    // pa->pb is the search edge.
    return ACROSSVERT;
  }

  pc = apex(*searchtet);
  if (pc == endpt) {
    // pa->pc is the search edge.
    eprevesymself(*searchtet);
    return ACROSSVERT;
  }

  // Walk through tets around pa until the right one is found.
  while (1) {

    pd = oppo(*searchtet);
    // Check whether the opposite vertex is 'endpt'.
    if (pd == endpt) {
      // pa->pd is the search edge.
      esymself(*searchtet);
      enextself(*searchtet);
      return ACROSSVERT;
    }
    // Check if we have entered outside of the domain.
    if (pd == dummypoint) {
      // This is possible when the mesh is non-convex.
      assert(nonconvex);
      return ACROSSSUB; // Hit a bounday.
    }

    // Now assume that the base face abc coincides with the horizon plane,
    //   and d lies above the horizon.  The search point 'endpt' may lie
    //   above or below the horizon.  We test the orientations of 'endpt'
    //   with respect to three planes: abc (horizon), bad (right plane),
    //   and acd (left plane). 
    hori = orient3d(pa, pb, pc, endpt);
    rori = orient3d(pb, pa, pd, endpt);
    lori = orient3d(pa, pc, pd, endpt);

    // Now decide the tet to move.  It is possible there are more than one
    //   tets are viable moves. Is so, randomly choose one. 
    if (hori > 0) {
      if (rori > 0) {
        if (lori > 0) {
          // Any of the three neighbors is a viable move.
          s = randomnation(3); 
          if (s == 0) {
            nextmove = HMOVE;
          } else if (s == 1) {
            nextmove = RMOVE;
          } else {
            nextmove = LMOVE;
          }
        } else {
          // Two tets, below horizon and below right, are viable.
          //s = randomnation(2); 
          if (randomnation(2)) {
            nextmove = HMOVE;
          } else {
            nextmove = RMOVE;
          }
        }
      } else {
        if (lori > 0) {
          // Two tets, below horizon and below left, are viable.
          //s = randomnation(2); 
          if (randomnation(2)) {
            nextmove = HMOVE;
          } else {
            nextmove = LMOVE;
          }
        } else {
          // The tet below horizon is chosen.
          nextmove = HMOVE;
        }
      }
    } else {
      if (rori > 0) {
        if (lori > 0) {
          // Two tets, below right and below left, are viable.
          //s = randomnation(2); 
          if (randomnation(2)) {
            nextmove = RMOVE;
          } else {
            nextmove = LMOVE;
          }
        } else {
          // The tet below right is chosen.
          nextmove = RMOVE;
        }
      } else {
        if (lori > 0) {
          // The tet below left is chosen.
          nextmove = LMOVE;
        } else {
          // 'endpt' lies either on the plane(s) or across face bcd.
          if (hori == 0) {
            if (rori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pb.
              return ACROSSVERT;
            }
            if (lori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pc.
              eprevesymself(*searchtet); // // [a,c,d]
              return ACROSSVERT;
            }
            // pa->'endpt' crosses the edge pb->pc.
            return ACROSSEDGE;
          }
          if (rori == 0) {
            if (lori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pd.
              esymself(*searchtet); // face bad.
              enextself(*searchtet); // face [a,d,b]
              return ACROSSVERT;
            }
            // pa->'endpt' crosses the edge pb->pd.
            esymself(*searchtet); // face bad.
            enextself(*searchtet); // face adb
            return ACROSSEDGE;
          }
          if (lori == 0) {
            // pa->'endpt' crosses the edge pc->pd.
            eprevesymself(*searchtet); // [a,c,d]
            return ACROSSEDGE;
          }
          // pa->'endpt' crosses the face bcd.
          return ACROSSFACE;
        }
      }
    }

    // Move to the next tet, fix pa as its origin.
    if (nextmove == RMOVE) {
      fnextself(*searchtet);
    } else if (nextmove == LMOVE) {
      eprevself(*searchtet);
      fnextself(*searchtet);
      enextself(*searchtet);
    } else { // HMOVE
      fsymself(*searchtet);
      enextself(*searchtet);
    }
    assert(org(*searchtet) == pa); 
    pb = dest(*searchtet);
    pc = apex(*searchtet);

  } // while (1)

}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsegment()    Search an edge in the tetrahedralization.               //
//                                                                           //
// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the   //
// edge from startpt to endpt.                                               //
//                                                                           //
// If the edge is missing, it returns either ACROSSEDGE or ACROSSFACE, which //
// indicates that the edge intersects an edge or a face.  If 'refpt' is nullptr,//
// 'searchtet' returns the edge or face. If 'refpt' is not nullptr, it returns  //
// a vertex which encroaches upon this edge, and 'searchtet' returns a tet   //
// which containing 'refpt'.                                                 // 
//                                                                           //
// The following cases can happen when the input PLC is not valid.           //
//   - ACROSSVERT, the edge intersects a vertex return by the origin of      //
//                 'searchtet'.                                              //
//   - ACROSSSEG, the edge intersects a segment returned by 'searchtet'.     //
//   - ACROSSSUB, the edge intersects a subface returned by 'searchtet'.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult 
  tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, 
                           point* refpt, arraypool* intfacelist)
{
  point pd;
  enum interresult dir;
  int t1ver;

  if (b->verbose > 2) {
    printf("      Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt));
  }

  point2tetorg(startpt, *searchtet);
  dir = finddirection(searchtet, endpt);

  if (dir == ACROSSVERT) {
    pd = dest(*searchtet);
    if (pd == endpt) {
      // The job is done. 
      return SHAREEDGE;
    } else {
      // A point is on the path.
      // Let the origin of the searchtet be the vertex.
      enextself(*searchtet);
      if (refpt) *refpt = pd;
      return ACROSSVERT;
    }
  } // if (dir == ACROSSVERT)

  // dir is either ACROSSEDGE or ACROSSFACE.

  enextesymself(*searchtet); // Go to the opposite face.
  fsymself(*searchtet); // Enter the adjacent tet.

  if (dir == ACROSSEDGE) {
    // Check whether two segments are intersecting.
    if (issubseg(*searchtet)) {
      return ACROSSSEG;
    }
  } else if (dir == ACROSSFACE) {
    if (checksubfaceflag) {
      // Check whether a segment and a subface are intersecting.
      if (issubface(*searchtet)) {
        return ACROSSSUB;
      }
    }
  }

  if (refpt == nullptr) {
    // Do not need a reference point. Return.
    return dir;
  }

  triface neightet, reftet;
  point pa, pb, pc;
  REAL angmax, ang;
  int types[2], poss[4];
  int pos = 0, i, j;

  pa = org(*searchtet);
  angmax = interiorangle(pa, startpt, endpt, nullptr);
  *refpt = pa;
  pb = dest(*searchtet);
  ang = interiorangle(pb, startpt, endpt, nullptr);
  if (ang > angmax) {
    angmax = ang;
    *refpt = pb;
  }
  pc = apex(*searchtet);
  ang = interiorangle(pc, startpt, endpt, nullptr);
  if (ang > angmax) {
    angmax = ang;
    *refpt = pc;
  }
  reftet = *searchtet; // Save the tet containing the refpt.

  // Search intersecting faces along the segment.
  while (1) {


    pd = oppo(*searchtet);
    assert(pd != dummypoint);  // SELF_CHECK


    // Stop if we meet 'endpt'.
    if (pd == endpt) break;

    ang = interiorangle(pd, startpt, endpt, nullptr);
    if (ang > angmax) {
      angmax = ang;
      *refpt = pd;
      reftet = *searchtet;
    }

    // Find a face intersecting the segment.
    if (dir == ACROSSFACE) {
      // One of the three oppo faces in 'searchtet' intersects the segment.
      neightet = *searchtet;
      j = (neightet.ver & 3); // j is the current face number.
      for (i = j + 1; i < j + 4; i++) {
        neightet.ver = (i % 4);
        pa = org(neightet);
        pb = dest(neightet);
        pc = apex(neightet);
        pd = oppo(neightet); // The above point.
        if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
          dir = (enum interresult) types[0];
          pos = poss[0];
          break;
        } else {
          dir = DISJOINT;
          pos = 0;
        }
      }
      assert(dir != DISJOINT);  // SELF_CHECK
    } else { // dir == ACROSSEDGE
      // Check the two opposite faces (of the edge) in 'searchtet'.      
      for (i = 0; i < 2; i++) {
        if (i == 0) {
          enextesym(*searchtet, neightet);
        } else {
          eprevesym(*searchtet, neightet);
        }
        pa = org(neightet);
        pb = dest(neightet);
        pc = apex(neightet);
        pd = oppo(neightet); // The above point.
        if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
          dir = (enum interresult) types[0];
          pos = poss[0];
          break;
        } else {
          dir = DISJOINT;
          pos = 0;
        }
      }
      if (dir == DISJOINT) {
        // No intersection. Rotate to the next tet at the edge.
        dir = ACROSSEDGE;
        fnextself(*searchtet);
        continue;
      }
    }

    if (dir == ACROSSVERT) {
      // This segment passing a vertex. Choose it and return.
      for (i = 0; i < pos; i++) {
        enextself(neightet);
      }
      pd = org(neightet);
      *refpt = pd;
      // break;
      return ACROSSVERT;
    } else if (dir == ACROSSEDGE) {
      // Get the edge intersects with the segment.
      for (i = 0; i < pos; i++) {
        enextself(neightet);
      }
    }
    // Go to the next tet.
    fsym(neightet, *searchtet);

    if (dir == ACROSSEDGE) {
      // Check whether two segments are intersecting.
      if (issubseg(*searchtet)) {
        return ACROSSSEG;
      }
    } else if (dir == ACROSSFACE) {
      if (checksubfaceflag) {
        // Check whether a segment and a subface are intersecting.
        if (issubface(*searchtet)) {
          return ACROSSSUB;
        }
      }
    }

  } // while (1)

  // A valid reference point should inside the diametrial circumsphere of
  //   the missing segment, i.e., it encroaches upon it.
  if (2.0 * angmax < PI) {
    *refpt = nullptr;
  }


  *searchtet = reftet;
  return dir;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsteinerpointonsegment()    Get a Steiner point on a segment.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt)
{
  point ei, ej;
  REAL Li, Lj, L;
  REAL t;
  int i;

  ei = sorg(*seg);
  ej = sdest(*seg);


  if (refpt != nullptr) {
    // Let ei be the closer one to refpt.
    Li = distance(ei, refpt);
    Lj = distance(ej, refpt);
    if (Li > Lj) {
      // Swap ei and ej;
      sesymself(*seg);
      ei = sorg(*seg);
      ej = sdest(*seg);
      L = Li;
      Li = Lj;
      Lj = L;
    }
    if (pointtype(ei) == ACUTEVERTEX) {
      // Cut the segment by a sphere centered at ei with radius Li. 
      L = distance(ei, ej);
      t = Li / L; // t \in (0, 1).
      for (i = 0; i < 3; i++) {
        steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
      }
      // Re-use Li and Lj;
      Li = distance(steinpt, refpt);
      Lj = distance(steinpt, ej);
      if (Li > Lj) {
        // Avoid to create a very short edge at ej.
        t = 0.5;
        for (i = 0; i < 3; i++) {
          steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
        }
      }
    } else {
      // Cut the segment by the projection point of refpt.
      projpt2edge(refpt, ei, ej, steinpt);
      L = distance(steinpt, refpt);
      if ((L > distance(steinpt, ei)) || (L > distance(steinpt, ej))) {
        // Avoid creating a very short edge.
        t = 0.5;
        for (i = 0; i < 3; i++) {
          steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
        }
      }
    }
  } else {
    // Split the point at the middle.
    t = 0.5;
    for (i = 0; i < 3; i++) {
      steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
    }
  } // if (refpt == nullptr)

  if (pointtype(steinpt) == UNUSEDVERTEX) {
    setpointtype(steinpt, FREESEGVERTEX);
  }
}



///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizesegments()    Recover segments in a DT.                          //
//                                                                           //
// All segments need to be recovered are in 'subsegstack' (Q).  They will be //
// be recovered one by one (in a random order).                              //
//                                                                           //
// Given a segment s in the Q, this routine first queries s in the DT, if s  //
// matches an edge in DT, it is 'locked' at the edge. Otherwise, s is split  //
// by inserting a new point p in both the DT and itself. The two new subseg- //
// ments of s are queued in Q.  The process continues until Q is empty.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizesegments()
{
  triface searchtet, spintet;
  face searchsh;
  face sseg, *psseg;
  point refpt, newpt;
  enum interresult dir;
  insertvertexflags ivf;
  int t1ver; 


  ivf.bowywat = 1; // Use Bowyer-Watson insertion.
  ivf.assignmeshsize = b->metric;
  ivf.sloc = (int) ONEDGE; // on 'sseg'.
  ivf.sbowywat = 1; // Use Bowyer-Watson insertion.

  // Loop until 'subsegstack' is empty.
  while (subsegstack->objects > 0l) {
    // seglist is used as a stack.
    subsegstack->objects--;
    psseg = (face *) fastlookup(subsegstack, subsegstack->objects);
    sseg = *psseg;

    // Check if this segment has been recovered.
    sstpivot1(sseg, searchtet);
    if (searchtet.tet != nullptr) {
      continue; // Not a missing segment.
    }

    // Search the segment.
    dir = scoutsegment(sorg(sseg), sdest(sseg), &searchtet, &refpt, nullptr);

    if (dir == SHAREEDGE) {
      // Found this segment, insert it.
      if (!issubseg(searchtet)) {
        // Let the segment remember an adjacent tet.
        sstbond1(sseg, searchtet);
        // Bond the segment to all tets containing it.
        spintet = searchtet;
        do {
          tssbond1(spintet, sseg);
          fnextself(spintet);
        } while (spintet.tet != searchtet.tet);
      } else {
        // Collision! Maybe a bug.
        assert(0);
      }
    } else {
      if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
        // The segment is missing. Split it.
        // Create a new point.
        makepoint(&newpt, FREESEGVERTEX);
        //setpointtype(newpt, FREESEGVERTEX);
        getsteinerptonsegment(&sseg, refpt, newpt);

        // Start searching from 'searchtet'.
        ivf.iloc = (int) OUTSIDE;
        // Insert the new point into the tetrahedralization T.
        //   Missing segments and subfaces are queued for recovery.
        //   Note that T is convex (nonconvex = 0).
        if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) {
          // The new point has been inserted.
          st_segref_count++;
          if (steinerleft > 0) steinerleft--;
        } else {
          assert (ivf.iloc == (enum locateresult) NEARVERTEX);
          terminatetetgen(4);
        }
      } else {
        // Indicate it is an input problem.
        terminatetetgen(3);
      }
    }
  } // while
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsubface()    Search subface in the tetrahedralization.               //
//                                                                           //
// 'searchsh' is searched in T. If it exists, it is 'locked' at the face in  //
// T. 'searchtet' refers to the face. Otherwise, it is missing.              //
//                                                                           //
// The return value indicates one of the following cases:                    //
//   - SHAREFACE, 'searchsh' exists and is inserted in T.                    //
//   - COLLISIONFACE, 'searchsh' exists in T, but it conflicts with another  //
//     subface which was inserted earlier. It is not inserted.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult 
  tetgenmesh::scoutsubface(face* searchsh, triface* searchtet)
{
  triface spintet;
  point pa, pb, pc;
  enum interresult dir;
  int t1ver; 

  pa = sorg(*searchsh);
  pb = sdest(*searchsh);


  // Get a tet whose origin is a.
  point2tetorg(pa, *searchtet);
  // Search the edge [a,b].
  dir = finddirection(searchtet, pb);
  if (dir == ACROSSVERT) {
    // Check validity of a PLC.
    if (dest(*searchtet) != pb) {
      // A vertex lies on the search edge. 
      enextself(*searchtet);
      // It is possible a PLC self-intersection problem.
      terminatetetgen(3);
      return TOUCHEDGE;
    }
    // The edge exists. Check if the face exists.
    pc = sapex(*searchsh);
    // Searchtet holds edge [a,b]. Search a face with apex c.
    spintet = *searchtet;
    while (1) {
      if (apex(spintet) == pc) {
        // Found a face matching to 'searchsh'!
        if (!issubface(spintet)) {
          // Insert 'searchsh'.
          tsbond(spintet, *searchsh);
          fsymself(spintet);
          sesymself(*searchsh);
          tsbond(spintet, *searchsh);
          *searchtet = spintet;
          return SHAREFACE;
        } else {
          // Another subface is already inserted.
          face checksh;
          tspivot(spintet, checksh);
          assert(checksh.sh != searchsh->sh); // SELF_CHECK
          // This is possibly an input problem, i.e., two facets overlap.
          // Report this problem and exit.
          printf("Warning:  Found two facets nearly overlap.\n");
          terminatetetgen(5);
          // unifysubfaces(&checksh, searchsh);
          *searchtet = spintet;
          return COLLISIONFACE;
        }
      }
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    }
  }

  // dir is either ACROSSEDGE or ACROSSFACE.
  return dir; 
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formregion()    Form the missing region of a missing subface.             //
//                                                                           //
// 'missh' is a missing subface. From it we form a missing region R which is //
// a connected region formed by a set of missing subfaces of a facet.        //
// Comment: There should be no segment inside R.                             //
//                                                                           //
// 'missingshs' returns the list of subfaces in R. All subfaces in this list //
// are oriented as the 'missh'.  'missingshbds' returns the list of boundary //
// edges (tetrahedral handles) of R.  'missingshverts' returns all vertices  //
// of R. They are all pmarktested.                                           //
//                                                                           //
// Except the first one (which is 'missh') in 'missingshs', each subface in  //
// this list represents an internal edge of R, i.e., it is missing in the    //
// tetrahedralization. Since R may contain interior vertices, not all miss-  //
// ing edges can be found by this way.                                       //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formregion(face* missh, arraypool* missingshs,  
                            arraypool* missingshbds, arraypool* missingshverts)
{
  triface searchtet, spintet;
  face neighsh, *parysh;
  face neighseg, fakeseg;
  point pa, pb, *parypt;
  enum interresult dir;
  int t1ver;
  int i, j;

  smarktest(*missh);
  missingshs->newindex((void **) &parysh);
  *parysh = *missh;

  // Incrementally find other missing subfaces.
  for (i = 0; i < missingshs->objects; i++) {
    missh = (face *) fastlookup(missingshs, i);
    for (j = 0; j < 3; j++) {
      pa = sorg(*missh);
      pb = sdest(*missh);
      point2tetorg(pa, searchtet);
      dir = finddirection(&searchtet, pb);
      if (dir != ACROSSVERT) {
        // This edge is missing. Its neighbor is a missing subface.
        spivot(*missh, neighsh);
        if (!smarktested(neighsh)) {
          // Adjust the face orientation.
          if (sorg(neighsh) != pb) sesymself(neighsh);
          smarktest(neighsh);
          missingshs->newindex((void **) &parysh);
          *parysh = neighsh;
        }
      } else {
        if (dest(searchtet) != pb) {
          // This might be a self-intersection problem.
          terminatetetgen(3); 
        }
      }
      // Collect the vertices of R.
      if (!pmarktested(pa)) {
        pmarktest(pa);
        missingshverts->newindex((void **) &parypt);
        *parypt = pa;
      }
      senextself(*missh);
    } // j
  } // i

  // Get the boundary edges of R.
  for (i = 0; i < missingshs->objects; i++) {
    missh = (face *) fastlookup(missingshs, i);
    for (j = 0; j < 3; j++) {
      spivot(*missh, neighsh);
      if ((neighsh.sh == nullptr) || !smarktested(neighsh)) {
        // A boundary edge of R.
        // Let the segment point to the adjacent tet.
        point2tetorg(sorg(*missh), searchtet);
        finddirection(&searchtet, sdest(*missh));
        missingshbds->newindex((void **) &parysh);
        *parysh = *missh;
        // Check if this edge is a segment.
        sspivot(*missh, neighseg); 
        if (neighseg.sh == nullptr) {
          // Temporarily create a segment at this edge.
          makeshellface(subsegs, &fakeseg);
          setsorg(fakeseg, sorg(*missh));
          setsdest(fakeseg, sdest(*missh));
          sinfect(fakeseg); // Mark it as faked.
          // Connect it to all tets at this edge.
          spintet = searchtet;
          while (1) {
            tssbond1(spintet, fakeseg);
            fnextself(spintet);
            if (spintet.tet == searchtet.tet) break;
          }
          neighseg = fakeseg;
        }
        // Let the segment and the boundary edge point to each other.
        ssbond(*missh, neighseg);
        sstbond1(neighseg, searchtet);
      }
      senextself(*missh);
    } // j
  } // i


  // Unmarktest collected missing subfaces.
  for (i = 0; i < missingshs->objects; i++) {
    parysh = (face *) fastlookup(missingshs, i);
    sunmarktest(*parysh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutcrossedge()    Search an edge that crosses the missing region.       //
//                                                                           //
// Return 1 if a crossing edge is found. It is returned by 'crosstet'. More- //
// over, the edge is oriented such that its origin lies below R.  Return 0   //
// if no such edge is found.                                                 //
//                                                                           //
// Assumption: All vertices of the missing region are marktested.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, 
                               arraypool* missingshs)
{
  triface searchtet, spintet;
  face *parysh;
  face neighseg;
  point pa, pb, pc, pd, pe;
  enum interresult dir;
  REAL ori;
  int types[2], poss[4];
  int searchflag, interflag;
  int t1ver;
  int i, j;

  searchflag = 0;

  for (j = 0; j < missingshbds->objects && !searchflag; j++) {
    parysh = (face *) fastlookup(missingshbds, j);
    sspivot(*parysh, neighseg);
    sstpivot1(neighseg, searchtet);
    interflag = 0;
    // Let 'spintet' be [#,#,d,e] where [#,#] is the boundary edge of R.
    spintet = searchtet;
    while (1) {
      pd = apex(spintet);
      pe = oppo(spintet);
      // Skip a hull edge.
      if ((pd != dummypoint) && (pe != dummypoint)) {
        // Skip an edge containing a vertex of R.
        if (!pmarktested(pd) && !pmarktested(pe)) {
          // Check if [d,e] intersects R.
          for (i = 0; i < missingshs->objects && !interflag; i++) {
            parysh = (face *) fastlookup(missingshs, i);
            pa = sorg(*parysh);
            pb = sdest(*parysh);
            pc = sapex(*parysh);
            interflag=tri_edge_test(pa, pb, pc, pd, pe, nullptr, 1, types, poss);
            if (interflag > 0) { 
              if (interflag == 2) {
                // They intersect at a single point.
                dir = (enum interresult) types[0];
                if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
                  //pos = poss[0];
                  // Go to the crossing edge [d,e,#,#].
                  edestoppo(spintet, crosstet); // // [d,e,#,#].
                  // Check if it is a segment.
                  if (issubseg(crosstet)) {
                    //face checkseg;
                    //tsspivot1(crosstet, checkseg);
                    //reportselfintersect(&checkseg, parysh);
                    terminatetetgen(3);
                  }
                  // Adjust the edge such that d lies below [a,b,c].
                  ori = orient3d(pa, pb, pc, pd);
                  assert(ori != 0);
                  if (ori < 0) {
                    esymself(crosstet);
                  }
                  searchflag = 1;                  
                }
              }
              break;
            } // if (interflag > 0)
          }
        } 
      }
      // Leave search at this bdry edge if an intersection is found.
      if (interflag > 0) break;
      // Go to the next tetrahedron.
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break; 
    } // while (1)
  } // j

  return searchflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formcavity()    Form the cavity of a missing region.                      //
//                                                                           //
// The missing region R is formed by a set of missing subfaces 'missingshs'. //
// In the following, we assume R is horizontal and oriented. (All subfaces   //
// of R are oriented in the same way.)  'searchtet' is a tetrahedron [d,e,#, //
// #] which intersects R in its interior, where the edge [d,e] intersects R, //
// and d lies below R.                                                       //
//                                                                           //
// 'crosstets' returns the set of crossing tets. Every tet in it has the     //
// form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R.  The   //
// set of tets form the cavity C, which is divided into two parts by R, one  //
// at top and one at bottom. 'topfaces' and 'botfaces' return the upper and  //
// lower boundary faces of C. 'toppoints' contains vertices of 'crosstets'   //
// in the top part of C, and so does 'botpoints'. Both 'toppoints' and       //
// 'botpoints' contain vertices of R.                                        //
//                                                                           //
// Important: This routine assumes all vertices of the facet containing this //
// subface are marked, i.e., pmarktested(p) returns true.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs,
                            arraypool* crosstets, arraypool* topfaces, 
                            arraypool* botfaces, arraypool* toppoints, 
                            arraypool* botpoints)
{
  arraypool *crossedges;
  triface spintet, neightet, *parytet;
  face *parysh = nullptr;
  point pa, pd, pe, *parypt;
  enum interresult dir; 
  bool testflag, invalidflag;
  int types[2], poss[4];
  int t1ver;
  int i, j, k;

  // Temporarily re-use 'topfaces' for all crossing edges.
  crossedges = topfaces;

  if (b->verbose > 2) {
    printf("      Form the cavity of a missing region.\n"); 
  }
  // Mark this edge to avoid testing it later.
  markedge(*searchtet);
  crossedges->newindex((void **) &parytet);
  *parytet = *searchtet;

  invalidflag = 0; 

  // Collect all crossing tets.  Each cross tet is saved in the standard
  //   form [d,e,#,#], where [d,e] is a corossing edge, d lies below R.
  //   NEITHER d NOR e is a vertex of R (!pmarktested). 
  for (i = 0; i < crossedges->objects; i++) {
    // Get a crossing edge [d,e,#,#].
    searchtet = (triface *) fastlookup(crossedges, i);

    // Sort vertices into the bottom and top arrays.
    pd = org(*searchtet);
    if (!pinfected(pd)) {
      pinfect(pd);
      botpoints->newindex((void **) &parypt);
      *parypt = pd;
    }
    pe = dest(*searchtet);
    if (!pinfected(pe)) {
      pinfect(pe);
      toppoints->newindex((void **) &parypt);
      *parypt = pe;
    }

    // All tets sharing this edge are crossing tets.
    spintet = *searchtet;
    while (1) {
      if (!infected(spintet)) {
        infect(spintet);
        crosstets->newindex((void **) &parytet);
        *parytet = spintet;
      }
      // Go to the next crossing tet.
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    } // while (1)

    // Detect new crossing edges.
    spintet = *searchtet;
    while (1) {
      // spintet is [d,e,a,#], where d lies below R, and e lies above R. 
      pa = apex(spintet);
      if (pa != dummypoint) {
        if (!pmarktested(pa)) {
              // There exists a crossing edge, either [e,a] or [a,d]. First check
          //   if the crossing edge has already be added, i.e., check if a
          //   tetrahedron at this edge is marked.
          testflag = true;
          for (j = 0; j < 2 && testflag; j++) {
            if (j == 0) {
              enext(spintet, neightet);
            } else {
              eprev(spintet, neightet);
            }
            while (1) {
              if (edgemarked(neightet)) {
                // This crossing edge has already been tested. Skip it.
                testflag = false;
                break;
              }
              fnextself(neightet);
              if (neightet.tet == spintet.tet) break;
            }
          } // j
          if (testflag) {
            // Test if [e,a] or [a,d] intersects R.
            // Do a brute-force search in the set of subfaces of R. Slow!
            //   Need to be improved!
            pd = org(spintet);
            pe = dest(spintet);
            for (k = 0; k < missingshs->objects; k++) {
              parysh = (face *) fastlookup(missingshs, k);
              if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh),
                                pe, pa, nullptr, 1, types, poss)) {
                // Found intersection. 'a' lies below R.
                enext(spintet, neightet);
                dir = (enum interresult) types[0];
                if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
                  // A valid intersection.
                } else {
                  // A non-valid intersection. Maybe a PLC problem.
                  invalidflag = 1;
                }
                break;
              }
              if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh),
                                pa, pd, nullptr, 1, types, poss)) {
                // Found intersection. 'a' lies above R.
                eprev(spintet, neightet);
                dir = (enum interresult) types[0];
                if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
                  // A valid intersection.
                } else {
                  // A non-valid intersection. Maybe a PLC problem.
                  invalidflag = 1;
                }
                break;
              }
            } // k
            if (k < missingshs->objects) {
              // Found a pair of triangle - edge interseciton.
              if (invalidflag) {
                if (!b->quiet) {
                  printf("Warning:  A non-valid facet - edge intersection\n");
                  printf("      subface: (%d, %d, %d) edge: (%d, %d)\n",
                         pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), 
                         pointmark(sapex(*parysh)), pointmark(org(neightet)),
                         pointmark(dest(neightet)));
                }
                // It may be a PLC problem.
                terminatetetgen(3);
              }
              // Adjust the edge direction, so that its origin lies below R,
              //   and its destination lies above R.
              esymself(neightet);
              // Check if this edge is a segment.
              if (issubseg(neightet)) {
                // Invalid PLC!
                //face checkseg;
                //tsspivot1(neightet, checkseg);
                //reportselfintersect(&checkseg, parysh);
                terminatetetgen(3);
              }
              // Mark this edge to avoid testing it again.
              markedge(neightet);
              crossedges->newindex((void **) &parytet);
              *parytet = neightet;            
            } else {
              // No intersection is found. It may be a PLC problem.
              invalidflag = 1;
              // Split the subface intersecting [d,e].
              for (k = 0; k < missingshs->objects; k++) {
                parysh = (face *) fastlookup(missingshs, k);
                // Test if this face intersects [e,a].
                if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh),
                                  pd, pe, nullptr, 1, types, poss)) {
                  break;
                }
              } // k
              if (k == missingshs->objects) {
                // Not found such an edge. 
                // Arbitrarily choose an edge (except the first) to split.
                k = randomnation(missingshs->objects - 1);
                parysh = (face *) fastlookup(missingshs, k + 1);
              }
              recentsh = *parysh;
              recenttet = spintet; // For point location.
              break; // the while (1) loop
            } // if (k == missingshs->objects)
          } // if (testflag)
            } // if (!pmarktested(pa) || b->psc)
      } // if (pa != dummypoint)
      // Go to the next crossing tet.
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    } // while (1)

    //if (b->psc) {
      if (invalidflag) break;
    //}
  } // i

  if (b->verbose > 2) {
    printf("      Formed cavity: %ld (%ld) cross tets (edges).\n", 
           crosstets->objects, crossedges->objects);
  }

  // Unmark all marked edges.
  for (i = 0; i < crossedges->objects; i++) {
    searchtet = (triface *) fastlookup(crossedges, i);
    assert(edgemarked(*searchtet)); // SELF_CHECK
    unmarkedge(*searchtet);
  }
  crossedges->restart();


  if (invalidflag) {
    // Unmark all collected tets.
    for (i = 0; i < crosstets->objects; i++) {
      searchtet = (triface *) fastlookup(crosstets, i);
      uninfect(*searchtet);
    }
    // Unmark all collected vertices.
    for (i = 0; i < botpoints->objects; i++) {
      parypt = (point *) fastlookup(botpoints, i);
      puninfect(*parypt);
    }
    for (i = 0; i < toppoints->objects; i++) {
      parypt = (point *) fastlookup(toppoints, i);
      puninfect(*parypt);
    }
    crosstets->restart();
    botpoints->restart();
    toppoints->restart();

    // Randomly split an interior edge of R.
    i = randomnation(missingshs->objects - 1);
    recentsh = * (face *) fastlookup(missingshs, i);
    return false;
  }


  // Collect the top and bottom faces and the middle vertices. Since all top
  //   and bottom vertices have been infected. Uninfected vertices must be
  //   middle vertices (i.e., the vertices of R).
  // NOTE 1: Hull tets may be collected. Process them as a normal one.
  // NOTE 2: Some previously recovered subfaces may be completely inside the
  //   cavity. In such case, we remove these subfaces from the cavity and put     //   them into 'subfacstack'. They will be recovered later.
  // NOTE 3: Some segments may be completely inside the cavity, e.g., they
  //   attached to a subface which is inside the cavity. Such segments are
  //   put in 'subsegstack'. They will be recovered later. 
  // NOTE4 : The interior subfaces and segments mentioned in NOTE 2 and 3
  //   are identified in the routine "carvecavity()". 

  for (i = 0; i < crosstets->objects; i++) {
    searchtet = (triface *) fastlookup(crosstets, i);
    // searchtet is [d,e,a,b].
    eorgoppo(*searchtet, spintet);
    fsym(spintet, neightet); // neightet is [a,b,e,#]
    if (!infected(neightet)) {
      // A top face.
      topfaces->newindex((void **) &parytet);
      *parytet = neightet;
    }
    edestoppo(*searchtet, spintet);
    fsym(spintet, neightet); // neightet is [b,a,d,#]
    if (!infected(neightet)) {
      // A bottom face.
      botfaces->newindex((void **) &parytet);
      *parytet = neightet;
    }
    // Add middle vertices if there are (skip dummypoint).
    pa = org(neightet);
    if (!pinfected(pa)) {
      if (pa != dummypoint) {
        pinfect(pa);
        botpoints->newindex((void **) &parypt);
        *parypt = pa;
        toppoints->newindex((void **) &parypt);
        *parypt = pa;
      }
    }
    pa = dest(neightet);
    if (!pinfected(pa)) {
      if (pa != dummypoint) {
        pinfect(pa);
        botpoints->newindex((void **) &parypt);
        *parypt = pa;
        toppoints->newindex((void **) &parypt);
        *parypt = pa;
      }
    }
  } // i

  // Uninfect all collected top, bottom, and middle vertices.
  for (i = 0; i < toppoints->objects; i++) {
    parypt = (point *) fastlookup(toppoints, i);
    puninfect(*parypt);
  }
  for (i = 0; i < botpoints->objects; i++) {
    parypt = (point *) fastlookup(botpoints, i);
    puninfect(*parypt);
  }
  cavitycount++;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizecavity()    Fill a cavity by Delaunay tetrahedra.                //
//                                                                           //
// The cavity C to be tetrahedralized is the top or bottom part of a whole   //
// cavity. 'cavfaces' contains the boundary faces of C. NOTE: faces in 'cav- //
// faces' do not form a closed polyhedron.  The "open" side are subfaces of  //
// the missing facet. These faces will be recovered later in fillcavity().   //
//                                                                           //
// This routine first constructs the DT of the vertices. Then it identifies  //
// the half boundary faces of the cavity in DT. Possiblely the cavity C will //
// be enlarged.                                                              //
//                                                                           //
// The DT is returned in 'newtets'.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, 
                                 arraypool *cavshells, arraypool *newtets, 
                                 arraypool *crosstets, arraypool *misfaces)
{
  triface searchtet, neightet, *parytet, *parytet1;
  face tmpsh, *parysh;
  point pa, pb, pc, pd, pt[3], *parypt;
  enum interresult dir;
  insertvertexflags ivf;
  REAL ori;
  long baknum, bakhullsize;
  int bakchecksubsegflag, bakchecksubfaceflag;
  int t1ver; 
  int i, j;

  if (b->verbose > 2) {
    printf("      Delaunizing cavity: %ld points, %ld faces.\n", 
           cavpoints->objects, cavfaces->objects);
  }
  // Remember the current number of crossing tets. It may be enlarged later.
  baknum = crosstets->objects;
  bakhullsize = hullsize;
  bakchecksubsegflag = checksubsegflag;
  bakchecksubfaceflag = checksubfaceflag;
  hullsize = 0l;
  checksubsegflag = 0;
  checksubfaceflag = 0;
  b->verbose--;  // Suppress informations for creating Delaunay tetra.
  b->plc = 0; // Do not check near vertices.

  ivf.bowywat = 1; // Use Bowyer-Watson algorithm.

  // Get four non-coplanar points (no dummypoint).
  pa = pb = pc = nullptr;
  for (i = 0; i < cavfaces->objects; i++) {
    parytet = (triface *) fastlookup(cavfaces, i);
    parytet->ver = epivot[parytet->ver];
    if (apex(*parytet) != dummypoint) {
      pa = org(*parytet);
      pb = dest(*parytet);
      pc = apex(*parytet);
      break;
    }
  }
  pd = nullptr;
  for (; i < cavfaces->objects; i++) {
    parytet = (triface *) fastlookup(cavfaces, i);
    pt[0] = org(*parytet);
    pt[1] = dest(*parytet);
    pt[2] = apex(*parytet);
    for (j = 0; j < 3; j++) {
      if (pt[j] != dummypoint) { // Do not include a hull point.
        ori = orient3d(pa, pb, pc, pt[j]);
        if (ori != 0) {
          pd = pt[j];
          if (ori > 0) {  // Swap pa and pb.
            pt[j] = pa; pa = pb; pb = pt[j]; 
          }
          break;
        }
      }
    }
    if (pd != nullptr) break;
  }
  assert(i < cavfaces->objects); // SELF_CHECK

  // Create an init DT.
  initialdelaunay(pa, pb, pc, pd);

  // Incrementally insert the vertices (duplicated vertices are ignored).
  for (i = 0; i < cavpoints->objects; i++) {
    pt[0] = * (point *) fastlookup(cavpoints, i);
    searchtet = recenttet;
    ivf.iloc = (int) OUTSIDE;
    insertpoint(pt[0], &searchtet, nullptr, nullptr, &ivf);
  }

  if (b->verbose > 2) {
    printf("      Identfying %ld boundary faces of the cavity.\n", 
           cavfaces->objects);
  }

  while (1) {

    // Identify boundary faces. Mark interior tets. Save missing faces.
    for (i = 0; i < cavfaces->objects; i++) {
      parytet = (triface *) fastlookup(cavfaces, i);
      // Skip an interior face (due to the enlargement of the cavity).
      if (infected(*parytet)) continue;
      parytet->ver = epivot[parytet->ver];
      pt[0] = org(*parytet);
      pt[1] = dest(*parytet);
      pt[2] = apex(*parytet);
      // Create a temp subface.
      makeshellface(subfaces, &tmpsh);
      setshvertices(tmpsh, pt[0], pt[1], pt[2]);
      // Insert tmpsh in DT.
      searchtet.tet = nullptr; 
      dir = scoutsubface(&tmpsh, &searchtet);
      if (dir == SHAREFACE) {
        // Inserted! 'tmpsh' must face toward the inside of the cavity.
        // Remember the boundary tet (outside the cavity) in tmpsh 
        //   (use the adjacent tet slot). 
        tmpsh.sh[0] = (shellface) encode(*parytet);
        // Save this subface.
        cavshells->newindex((void **) &parysh);
        *parysh = tmpsh;
      } 
      else {
        // This boundary face is missing.
        shellfacedealloc(subfaces, tmpsh.sh);
        // Save this face in list.
        misfaces->newindex((void **) &parytet1);
        *parytet1 = *parytet;
      }
    } // i

    if (misfaces->objects > 0) {
      if (b->verbose > 2) {
        printf("      Enlarging the cavity. %ld missing bdry faces\n", 
               misfaces->objects);
      }

      // Removing all tempoaray subfaces.
      for (i = 0; i < cavshells->objects; i++) {
        parysh = (face *) fastlookup(cavshells, i);
        stpivot(*parysh, neightet);
        tsdissolve(neightet); // Detach it from adj. tets.
        fsymself(neightet);
        tsdissolve(neightet);
        shellfacedealloc(subfaces, parysh->sh);
      }
      cavshells->restart();

      // Infect the points which are of the cavity.
      for (i = 0; i < cavpoints->objects; i++) {
        pt[0] = * (point *) fastlookup(cavpoints, i);
        pinfect(pt[0]); // Mark it as inserted.
      }

      // Enlarge the cavity.
      for (i = 0; i < misfaces->objects; i++) {
        // Get a missing face.
        parytet = (triface *) fastlookup(misfaces, i);
        if (!infected(*parytet)) {
          // Put it into crossing tet list.
          infect(*parytet);
          crosstets->newindex((void **) &parytet1);
          *parytet1 = *parytet;
          // Insert the opposite point if it is not in DT.
          pd = oppo(*parytet);
          if (!pinfected(pd)) {
            searchtet = recenttet;
            ivf.iloc = (int) OUTSIDE;
            insertpoint(pd, &searchtet, nullptr, nullptr, &ivf);
            pinfect(pd);
            cavpoints->newindex((void **) &parypt);
            *parypt = pd;
          }
          // Add three opposite faces into the boundary list.
          for (j = 0; j < 3; j++) {
            esym(*parytet, neightet);
            fsymself(neightet);
            if (!infected(neightet)) {
              cavfaces->newindex((void **) &parytet1);
              *parytet1 = neightet;
            } 
            enextself(*parytet);
          } // j
        } // if (!infected(parytet))
      } // i

      // Uninfect the points which are of the cavity.
      for (i = 0; i < cavpoints->objects; i++) {
        pt[0] = * (point *) fastlookup(cavpoints, i);
        puninfect(pt[0]);
      }

      misfaces->restart();
      continue;
    } // if (misfaces->objects > 0)

    break;

  } // while (1)

  // Collect all tets of the DT. All new tets are marktested.
  marktest(recenttet);
  newtets->newindex((void **) &parytet);
  *parytet = recenttet;
  for (i = 0; i < newtets->objects; i++) {
    searchtet = * (triface *) fastlookup(newtets, i);
    for (j = 0; j < 4; j++) {
      decode(searchtet.tet[j], neightet);
      if (!marktested(neightet)) {
        marktest(neightet);
        newtets->newindex((void **) &parytet);
        *parytet = neightet;
      }
    }
  }

  cavpoints->restart();
  cavfaces->restart();

  if (crosstets->objects > baknum) {
    // The cavity has been enlarged.
    cavityexpcount++;
  }

  // Restore the original values.
  hullsize = bakhullsize;
  checksubsegflag = bakchecksubsegflag;
  checksubfaceflag = bakchecksubfaceflag;
  b->verbose++;
  b->plc = 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// fillcavity()    Fill new tets into the cavity.                            //
//                                                                           //
// The new tets are stored in two disjoint sets(which share the same facet). //
// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect-  //
// ively. 'midfaces' is empty on input, and will store faces in the facet.   //
//                                                                           //
// Important: This routine assumes all vertices of the missing region R are  //
// marktested, i.e., pmarktested(p) returns true.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells,
                            arraypool* midfaces, arraypool* missingshs,
                            arraypool* topnewtets, arraypool* botnewtets,
                            triface* crossedge)
{
  arraypool *cavshells;
  triface bdrytet, neightet, *parytet;
  triface searchtet, spintet;
  face *parysh;
  face checkseg;
  point pa, pb, pc;
  bool mflag;
  int t1ver;
  int i, j;

  // Connect newtets to tets outside the cavity.  These connections are needed
  //   for identifying the middle faces (which belong to R).
  for (j = 0; j < 2; j++) {
    cavshells = (j == 0 ? topshells : botshells);
    if (cavshells != nullptr) {
      for (i = 0; i < cavshells->objects; i++) {
        // Get a temp subface.
        parysh = (face *) fastlookup(cavshells, i);
        // Get the boundary tet outside the cavity (saved in sh[0]).
        decode(parysh->sh[0], bdrytet);
        pa = org(bdrytet);
        pb = dest(bdrytet);
        pc = apex(bdrytet);
        // Get the adjacent new tet inside the cavity.
        stpivot(*parysh, neightet);
        // Mark neightet as an interior tet of this cavity.
        infect(neightet);
        // Connect the two tets (the old connections are replaced).
        bond(bdrytet, neightet); 
        tsdissolve(neightet); // Clear the pointer to tmpsh.
        // Update the point-to-tets map.
        setpoint2tet(pa, (tetrahedron) neightet.tet);
        setpoint2tet(pb, (tetrahedron) neightet.tet);
        setpoint2tet(pc, (tetrahedron) neightet.tet);
      } // i
    } // if (cavshells != nullptr)
  } // j

  if (crossedge != nullptr) {
    // Glue top and bottom tets at their common facet.
    triface toptet, bottet, spintet, *midface;
    point pd, pe;
    REAL ori;
    int types[2], poss[4];
    int interflag;
    int bflag;

    mflag = false;
    pd = org(*crossedge);
    pe = dest(*crossedge);

    // Search the first (middle) face in R. 
    // Since R may be non-convex, we must make sure that the face is in the
    //   interior of R.  We search a face in 'topnewtets' whose three vertices
    //   are on R and it intersects 'crossedge' in its interior. Then search
    //   a matching face in 'botnewtets'.
    for (i = 0; i < topnewtets->objects && !mflag; i++) {
      searchtet = * (triface *) fastlookup(topnewtets, i);
      for (searchtet.ver = 0; searchtet.ver < 4 && !mflag; searchtet.ver++) {
        pa = org(searchtet);
        if (pmarktested(pa)) {
          pb = dest(searchtet);
          if (pmarktested(pb)) {
            pc = apex(searchtet);
            if (pmarktested(pc)) {
              // Check if this face intersects [d,e].
              interflag = tri_edge_test(pa,pb,pc,pd,pe,nullptr,1,types,poss);
              if (interflag == 2) {
                // They intersect at a single point. Found.
                toptet = searchtet;
                // The face lies in the interior of R.
                // Get the tet (in topnewtets) which lies above R.
                ori = orient3d(pa, pb, pc, pd);
                assert(ori != 0);
                if (ori < 0) {
                  fsymself(toptet);
                  pa = org(toptet);
                  pb = dest(toptet);
                }
                // Search the face [b,a,c] in 'botnewtets'.
                for (j = 0; j < botnewtets->objects; j++) {
                  neightet = * (triface *) fastlookup(botnewtets, j);                  
                  // Is neightet contains 'b'.
                  if ((point) neightet.tet[4] == pb) {
                    neightet.ver = 11;
                  } else if ((point) neightet.tet[5] == pb) {
                    neightet.ver = 3;
                  } else if ((point) neightet.tet[6] == pb) {
                    neightet.ver = 7;
                  } else if ((point) neightet.tet[7] == pb) {
                    neightet.ver = 0;
                  } else {
                    continue;
                  }
                  // Is the 'neightet' contains edge [b,a].
                  if (dest(neightet) == pa) {
                    // 'neightet' is just the edge.
                  } else if (apex(neightet) == pa) {
                    eprevesymself(neightet);
                  } else if (oppo(neightet) == pa) {
                    esymself(neightet);
                    enextself(neightet);
                  } else {
                    continue;
                  }
                  // Is 'neightet' the face [b,a,c]. 
                  if (apex(neightet) == pc) {
                    bottet = neightet;
                    mflag = true;
                    break;
                  }
                } // j
              } // if (interflag == 2)
            } // pc
          } // pb
        } // pa
      } // toptet.ver
    } // i

    if (mflag) {
      // Found a pair of matched faces in 'toptet' and 'bottet'.
      bond(toptet, bottet);
      // Both are interior tets.
      infect(toptet);
      infect(bottet);
      // Add this face into search list.
      markface(toptet);
      midfaces->newindex((void **) &parytet);
      *parytet = toptet;
    } else {
      // No pair of 'toptet' and 'bottet'.
      toptet.tet = nullptr;
      // Randomly split an interior edge of R.
      i = randomnation(missingshs->objects - 1);
      recentsh = * (face *) fastlookup(missingshs, i);
    }

    // Find other middle faces, connect top and bottom tets.
    for (i = 0; i < midfaces->objects && mflag; i++) {
      // Get a matched middle face [a, b, c]
      midface = (triface *) fastlookup(midfaces, i);
      // The tet must be a new created tet (marktested).
      assert(marktested(*midface)); // SELF_CHECK
      // Check the neighbors at the edges of this face. 
      for (j = 0; j < 3 && mflag; j++) {
        toptet = *midface;
        bflag = false;
        while (1) {
          // Go to the next face in the same tet.
          esymself(toptet);
          pc = apex(toptet);
          if (pmarktested(pc)) {
            break; // Find a subface.
          }
          if (pc == dummypoint) {
            assert(0); // Check this case.
            break; // Find a subface.
          }
          // Go to the adjacent tet.
          fsymself(toptet);
          // Do we walk outside the cavity? 
          if (!marktested(toptet)) {
            // Yes, the adjacent face is not a middle face.
            bflag = true; break; 
          }
        }
        if (!bflag) {
          // assert(marktested(toptet)); // SELF_CHECK
          if (!facemarked(toptet)) {
            fsym(*midface, bottet);
            spintet = bottet;
            while (1) {
              esymself(bottet);
              pd = apex(bottet);
              if (pd == pc) break; // Face matched.
              fsymself(bottet);
              if (bottet.tet == spintet.tet) {
                // Not found a matched bottom face.
                mflag = false;
                break;
              }
            } // while (1)
            if (mflag) {
              if (marktested(bottet)) {
                // Connect two tets together.
                bond(toptet, bottet);
                // Both are interior tets.
                infect(toptet);
                infect(bottet);
                // Add this face into list.
                markface(toptet);
                midfaces->newindex((void **) &parytet);
                *parytet = toptet;
              }
            } else { // mflag == false
              // Adjust 'toptet' and 'bottet' to be the crossing edges.
              fsym(*midface, bottet);
              spintet = bottet;
              while (1) {
                esymself(bottet);
                pd = apex(bottet);
                if (pmarktested(pd)) {
                  // assert(pd != pc);
                  // Let 'toptet' be [a,b,c,#], and 'bottet' be [b,a,d,*].
                  // Adjust 'toptet' and 'bottet' to be the crossing edges.
                  // Test orient3d(b,c,#,d).
                  ori = orient3d(dest(toptet), pc, oppo(toptet), pd);
                  if (ori < 0) {
                    // Edges [a,d] and [b,c] cross each other.
                    enextself(toptet); // [b,c]
                    enextself(bottet); // [a,d]
                  } else if (ori > 0) {
                    // Edges [a,c] and [b,d] cross each other. 
                    eprevself(toptet); // [c,a]
                    eprevself(bottet); // [d,b]
                  } else {
                    // b,c,#,and d are coplanar!.
                    assert(0);
                  }
                  break; // Not matched
                }
                fsymself(bottet);
                assert (bottet.tet != spintet.tet);
              }
            } // if (!mflag)
          } // if (!facemarked(toptet))
        } // if (!bflag)
        enextself(*midface);
      } // j
    } // i

    if (mflag) {
      if (b->verbose > 2) {
        printf("      Found %ld middle subfaces.\n", midfaces->objects);
      }
      face oldsh, newsh, casout, casin, neighsh;

      oldsh = * (face *) fastlookup(missingshs, 0);

      // Create new subfaces to fill the region R.
      for (i = 0; i < midfaces->objects; i++) {
        // Get a matched middle face [a, b, c]
        midface = (triface *) fastlookup(midfaces, i);
        unmarkface(*midface);
        makeshellface(subfaces, &newsh);
        setsorg(newsh, org(*midface));
        setsdest(newsh, dest(*midface));
        setsapex(newsh, apex(*midface));
        // The new subface gets its markers from the old one.
        setshellmark(newsh, shellmark(oldsh));
        if (checkconstraints) {
          setareabound(newsh, areabound(oldsh));
        }
        // Connect the new subface to adjacent tets.
        tsbond(*midface, newsh);
        fsym(*midface, neightet);
        sesymself(newsh);
        tsbond(neightet, newsh);
      }

      // Connect new subfaces together and to the bdry of R.
      // Delete faked segments.
      for (i = 0; i < midfaces->objects; i++) {
        // Get a matched middle face [a, b, c]
        midface = (triface *) fastlookup(midfaces, i);
        for (j = 0; j < 3; j++) {
          tspivot(*midface, newsh);
          spivot(newsh, casout);
          if (casout.sh == nullptr) {
            // Search its neighbor.
            fnext(*midface, searchtet);
            while (1) {
              // (1) First check if this side is a bdry edge of R.
              tsspivot1(searchtet, checkseg);
              if (checkseg.sh != nullptr) {
                // It's a bdry edge of R.
                assert(!infected(searchtet)); // It must not be a cavity tet.
                // Get the old subface.
                checkseg.shver = 0;
                spivot(checkseg, oldsh);
                if (sinfected(checkseg)) {
                  // It's a faked segment. Delete it.
                  spintet = searchtet;
                  while (1) {
                    tssdissolve1(spintet);
                    fnextself(spintet);
                    if (spintet.tet == searchtet.tet) break;
                  }
                  shellfacedealloc(subsegs, checkseg.sh);
                  ssdissolve(oldsh);
                  checkseg.sh = nullptr;
                }
                spivot(oldsh, casout);
                if (casout.sh != nullptr) {
                  casin = casout;
                  if (checkseg.sh != nullptr) {
                    // Make sure that the subface has the right ori at the 
                    //   segment.
                    checkseg.shver = 0;
                    if (sorg(newsh) != sorg(checkseg)) {
                      sesymself(newsh);
                    }
                    spivot(casin, neighsh);
                    while (neighsh.sh != oldsh.sh) {
                      casin = neighsh;
                      spivot(casin, neighsh);
                    }
                  }
                  sbond1(newsh, casout);
                  sbond1(casin, newsh);
                }
                if (checkseg.sh != nullptr) {
                  ssbond(newsh, checkseg);
                }
                break;
              } // if (checkseg.sh != nullptr)
              // (2) Second check if this side is an interior edge of R.
              tspivot(searchtet, neighsh);
              if (neighsh.sh != nullptr) {
                // Found an adjacent subface of newsh (an interior edge).
                sbond(newsh, neighsh);
                break;
              }
              fnextself(searchtet);
              assert(searchtet.tet != midface->tet);
            } // while (1)
          } // if (casout.sh == nullptr)
          enextself(*midface);
        } // j
      } // i

      // Delete old subfaces.
      for (i = 0; i < missingshs->objects; i++) {
        parysh = (face *) fastlookup(missingshs, i);
        shellfacedealloc(subfaces, parysh->sh);
      }
    } else {
      if (toptet.tet != nullptr) {
        // Faces at top and bottom are not matched. 
        // Choose a Steiner point in R.
        // Split one of the crossing edges.
        pa = org(toptet);
        pb = dest(toptet);
        pc = org(bottet);
        pd = dest(bottet);
        // Search an edge in R which is either [a,b] or [c,d].
        // Reminder:  Subfaces in this list 'missingshs', except the first
        //   one, represents an interior edge of R. 
        for (i = 1; i < missingshs->objects; i++) {
          parysh = (face *) fastlookup(missingshs, i);
          if (((sorg(*parysh) == pa) && (sdest(*parysh) == pb)) ||
              ((sorg(*parysh) == pb) && (sdest(*parysh) == pa))) break;
          if (((sorg(*parysh) == pc) && (sdest(*parysh) == pd)) ||
              ((sorg(*parysh) == pd) && (sdest(*parysh) == pc))) break;
        }
        if (i < missingshs->objects) {
          // Found. Return it.
          recentsh = *parysh;
        } else {
          assert(0);
        }
      }
    }

    midfaces->restart();
  } else {
    mflag = true;
  } 

  // Delete the temp subfaces.
  for (j = 0; j < 2; j++) {
    cavshells = (j == 0 ? topshells : botshells);
    if (cavshells != nullptr) {
      for (i = 0; i < cavshells->objects; i++) {
        parysh = (face *) fastlookup(cavshells, i);
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
  }

  topshells->restart();
  if (botshells != nullptr) {
    botshells->restart();
  }

  return mflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carvecavity()    Delete old tets and outer new tets of the cavity.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets,
                             arraypool *botnewtets)
{
  arraypool *newtets;
  shellface *sptr, *ssptr;
  triface *parytet, *pnewtet, newtet, neightet, spintet;
  face checksh, *parysh;
  face checkseg, *paryseg;
  int t1ver;
  int i, j;

  if (b->verbose > 2) {
    printf("      Carve cavity: %ld old tets.\n", crosstets->objects);
  }

  // First process subfaces and segments which are adjacent to the cavity.
  //   They must be re-connected to new tets in the cavity.
  // Comment: It is possible that some subfaces and segments are completely
  //   inside the cavity. This can happen even if the cavity is not enlarged. 
  //   Before deleting the old tets, find and queue all interior subfaces
  //   and segments. They will be recovered later. 2010-05-06.

  // Collect all subfaces and segments which attached to the old tets.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    if ((sptr = (shellface*) parytet->tet[9]) != nullptr) {
      for (j = 0; j < 4; j++) {
        if (sptr[j]) {
          sdecode(sptr[j], checksh);
          if (!sinfected(checksh)) {
            sinfect(checksh);
            cavetetshlist->newindex((void **) &parysh);
            *parysh = checksh;
          }
        }
      } // j
    }
    if ((ssptr = (shellface*) parytet->tet[8]) != nullptr) {
      for (j = 0; j < 6; j++) {
        if (ssptr[j]) {
          sdecode(ssptr[j], checkseg);
          // Skip a deleted segment (was a faked segment)
          if (checkseg.sh[3] != nullptr) {
            if (!sinfected(checkseg)) {
              sinfect(checkseg);
              cavetetseglist->newindex((void **) &paryseg);
              *paryseg = checkseg;
            }
          }
        }
      } // j
    }
  } // i

  // Uninfect collected subfaces.
  for (i = 0; i < cavetetshlist->objects; i++) {
    parysh = (face *) fastlookup(cavetetshlist, i);
    suninfect(*parysh);
  }
  // Uninfect collected segments.
  for (i = 0; i < cavetetseglist->objects; i++) {
    paryseg = (face *) fastlookup(cavetetseglist, i);
    suninfect(*paryseg);
  }

  // Connect subfaces to new tets.
  for (i = 0; i < cavetetshlist->objects; i++) {
    parysh = (face *) fastlookup(cavetetshlist, i);
    // Get an adjacent tet at this subface.
    stpivot(*parysh, neightet);
    // Does this tet lie inside the cavity.
    if (infected(neightet)) {
      // Yes. Get the other adjacent tet at this subface.
      sesymself(*parysh);
      stpivot(*parysh, neightet);
      // Does this tet lie inside the cavity.
      if (infected(neightet)) {
        checksh = *parysh;
        stdissolve(checksh);
        caveencshlist->newindex((void **) &parysh);
        *parysh = checksh;
      }
    }
    if (!infected(neightet)) {
      // Found an outside tet. Re-connect this subface to a new tet.
      fsym(neightet, newtet);
      assert(marktested(newtet)); // It's a new tet.
      sesymself(*parysh);
      tsbond(newtet, *parysh);
    }
  } // i


  for (i = 0; i < cavetetseglist->objects; i++) {
    checkseg = * (face *) fastlookup(cavetetseglist, i);
    // Check if the segment is inside the cavity.
    sstpivot1(checkseg, neightet);
    spintet = neightet;
    while (1) {
      if (!infected(spintet)) {
        // This segment is on the boundary of the cavity.
        break;
      }
      fnextself(spintet);
      if (spintet.tet == neightet.tet) {
        sstdissolve1(checkseg);
        caveencseglist->newindex((void **) &paryseg);
        *paryseg = checkseg;
        break;
      }
    }
    if (!infected(spintet)) {
      // A boundary segment. Connect this segment to the new tets.
      sstbond1(checkseg, spintet);
      neightet = spintet;
      while (1) {
        tssbond1(spintet, checkseg);
        fnextself(spintet);
        if (spintet.tet == neightet.tet) break;
      }
    }
  } // i


  cavetetshlist->restart();
  cavetetseglist->restart();

  // Delete the old tets in cavity.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    if (ishulltet(*parytet)) {
      hullsize--;
    }
    tetrahedrondealloc(parytet->tet);
  }

  crosstets->restart(); // crosstets will be re-used.

  // Collect new tets in cavity.  Some new tets have already been found 
  //   (and infected) in the fillcavity(). We first collect them.
  for (j = 0; j < 2; j++) {
    newtets = (j == 0 ? topnewtets : botnewtets);
    if (newtets != nullptr) {
      for (i = 0; i < newtets->objects; i++) {
        parytet = (triface *) fastlookup(newtets, i);
        if (infected(*parytet)) {
          crosstets->newindex((void **) &pnewtet);
          *pnewtet = *parytet;
        }
      } // i
    }
  } // j

  // Now we collect all new tets in cavity.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    for (j = 0; j < 4; j++) {
      decode(parytet->tet[j], neightet);
      if (marktested(neightet)) { // Is it a new tet?
        if (!infected(neightet)) {
          // Find an interior tet.
          //assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK
          infect(neightet);
          crosstets->newindex((void **) &pnewtet);
          *pnewtet = neightet;
        }
      }
    } // j
  } // i

  parytet = (triface *) fastlookup(crosstets, 0);
  recenttet = *parytet; // Remember a live handle.

  // Delete outer new tets.
  for (j = 0; j < 2; j++) {
    newtets = (j == 0 ? topnewtets : botnewtets);
    if (newtets != nullptr) {
      for (i = 0; i < newtets->objects; i++) {
        parytet = (triface *) fastlookup(newtets, i);
        if (infected(*parytet)) {
          // This is an interior tet.
          uninfect(*parytet);
          unmarktest(*parytet);
          if (ishulltet(*parytet)) {
            hullsize++;
          }
        } else {
          // An outer tet. Delete it.
          tetrahedrondealloc(parytet->tet);
        }
      }
    }
  }

  crosstets->restart();
  topnewtets->restart();
  if (botnewtets != nullptr) {
    botnewtets->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restorecavity()    Reconnect old tets and delete new tets of the cavity.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets,
                               arraypool *botnewtets, arraypool *missingshbds)
{
  triface *parytet, neightet, spintet;
  face *parysh;
  face checkseg;
  point *ppt;
  int t1ver;
  int i, j;

  // Reconnect crossing tets to cavity boundary.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    assert(infected(*parytet)); // SELF_CHECK
    parytet->ver = 0;
    for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
      fsym(*parytet, neightet);
      if (!infected(neightet)) {
        // Restore the old connections of tets.
        bond(*parytet, neightet);
      }
    }
    // Update the point-to-tet map.
    parytet->ver = 0;
    ppt = (point *) &(parytet->tet[4]);
    for (j = 0; j < 4; j++) {
      setpoint2tet(ppt[j], encode(*parytet));
    }
  }

  // Uninfect all crossing tets.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    uninfect(*parytet);
  }

  // Remember a live handle.
  recenttet = * (triface *) fastlookup(crosstets, 0);

  // Delete faked segments.
  for (i = 0; i < missingshbds->objects; i++) {
    parysh = (face *) fastlookup(missingshbds, i);
    sspivot(*parysh, checkseg);
    assert(checkseg.sh != nullptr);
    if (checkseg.sh[3] != nullptr) {
      if (sinfected(checkseg)) {
            // It's a faked segment. Delete it.
        sstpivot1(checkseg, neightet);
        spintet = neightet;
        while (1) {
          tssdissolve1(spintet);
          fnextself(spintet);
          if (spintet.tet == neightet.tet) break;
        }
        shellfacedealloc(subsegs, checkseg.sh);
        ssdissolve(*parysh);
        //checkseg.sh = nullptr;
      }
    }
  } // i

  // Delete new tets.
  for (i = 0; i < topnewtets->objects; i++) {
    parytet = (triface *) fastlookup(topnewtets, i);
    tetrahedrondealloc(parytet->tet);
  }

  if (botnewtets != nullptr) {
    for (i = 0; i < botnewtets->objects; i++) {
      parytet = (triface *) fastlookup(botnewtets, i);
      tetrahedrondealloc(parytet->tet);
    }
  }

  crosstets->restart();
  topnewtets->restart();
  if (botnewtets != nullptr) {
    botnewtets->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipcertify()    Insert a crossing face into priority queue.              //
//                                                                           //
// A crossing face of a facet must have at least one top and one bottom ver- //
// tex of the facet.                                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa,
                             point plane_pb, point plane_pc)
{
  badface *parybf, *prevbf, *nextbf;
  triface neightet;
  face checksh;
  point p[5];
  REAL w[5];
  REAL insph, ori4;
  int topi, boti;
  int i;

  // Compute the flip time \tau.
  fsym(*chkface, neightet);

  p[0] = org(*chkface);
  p[1] = dest(*chkface);
  p[2] = apex(*chkface);
  p[3] = oppo(*chkface);
  p[4] = oppo(neightet);

  // Check if the face is a crossing face.
  topi = boti = 0;
  for (i = 0; i < 3; i++) {
    if (pmarktest2ed(p[i])) topi++;
    if (pmarktest3ed(p[i])) boti++;
  }
  if ((topi == 0) || (boti == 0)) {
    // It is not a crossing face.
    // return;
    for (i = 3; i < 5; i++) {
      if (pmarktest2ed(p[i])) topi++;
      if (pmarktest3ed(p[i])) boti++;
    }
    if ((topi == 0) || (boti == 0)) {
      // The two tets sharing at this face are on one side of the facet.
      // Check if this face is locally Delaunay (due to rounding error).
      if ((p[3] != dummypoint) && (p[4] != dummypoint)) {
        // Do not check it if it is a subface.
        tspivot(*chkface, checksh);
        if (checksh.sh == nullptr) {
          insph = insphere_s(p[1], p[0], p[2], p[3], p[4]);
          assert(insph != 0);
          if (insph > 0) {
            // Add the face into queue.
            if (b->verbose > 2) {
              printf("      A locally non-Delanay face (%d, %d, %d)-%d,%d\n", 
                     pointmark(p[0]), pointmark(p[1]), pointmark(p[2]), 
                     pointmark(p[3]), pointmark(p[4]));
            }
            parybf = (badface *) flippool->alloc();
            parybf->key = 0.;  // tau = 0, do immediately.
            parybf->tt = *chkface;
            parybf->forg = p[0];
            parybf->fdest = p[1];
            parybf->fapex = p[2];
            parybf->foppo = p[3];
            parybf->noppo = p[4];
            // Add it at the top of the priority queue.
            if (*pqueue == nullptr) {
              *pqueue = parybf;
              parybf->nextitem = nullptr;
            } else {
              parybf->nextitem = *pqueue;
              *pqueue = parybf;
            }
          } // if (insph > 0)
        } // if (checksh.sh == nullptr)
      }
      //return;
    }
    return; // Test: omit this face.
  }

  // Decide the "height" for each point.
  for (i = 0; i < 5; i++) {
    if (pmarktest2ed(p[i])) {
      // A top point has a positive weight.
      w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]);      
      if (w[i] < 0) w[i] = -w[i];
      assert(w[i] != 0);
    } else {
      w[i] = 0;
    }
  }

  // Make sure orient3d(p[1], p[0], p[2], p[3]) > 0;
  //   Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that
  //     p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3].
  //   The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that
  //     p[4] lies below the oriented hyperplane passing through 
  //     p[1], p[0], p[2], p[3].

  insph = insphere(p[1], p[0], p[2], p[3], p[4]);
  ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]);

  if (b->verbose > 2) {
    printf("      Heights: (%g, %g, %g, %g, %g)\n", w[0],w[1],w[2],w[3],w[4]);
    printf("      Insph: %g, ori4: %g, tau = %g\n", insph, ori4, -insph/ori4);
  }

  if (ori4 > 0) {
    // Add the face into queue.
    if (b->verbose > 2) {
      printf("      Insert face (%d, %d, %d) - %d, %d\n", pointmark(p[0]),
        pointmark(p[1]), pointmark(p[2]), pointmark(p[3]), pointmark(p[4]));
    }
    
    parybf = (badface *) flippool->alloc();

    parybf->key = -insph / ori4;
    parybf->tt = *chkface;
    parybf->forg = p[0];
    parybf->fdest = p[1];
    parybf->fapex = p[2];
    parybf->foppo = p[3];
    parybf->noppo = p[4];

    // Push the face into priority queue.
    //pq.push(bface);
    if (*pqueue == nullptr) {
      *pqueue = parybf;
      parybf->nextitem = nullptr;
    } else {
      // Search an item whose key is larger or equal to current key.
      prevbf = nullptr;
      nextbf = *pqueue;
      //if (!b->flipinsert_random) { // Default use a priority queue.
        // Insert the item into priority queue.
        while (nextbf != nullptr) {
          if (nextbf->key < parybf->key) {
            prevbf = nextbf;
            nextbf = nextbf->nextitem;
          } else {
            break;
          }
        }
      //} // if (!b->flipinsert_random)
      // Insert the new item between prev and next items.
      if (prevbf == nullptr) {
        *pqueue = parybf;
      } else {
        prevbf->nextitem = parybf;
      }
      parybf->nextitem = nextbf;
    }
  } else if (ori4 == 0) {
    
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipinsertfacet()    Insert a facet into a CDT by flips.                  //
//                                                                           //
// The algorithm is described in Shewchuk's paper "Updating and Constructing //
// Constrained Delaunay and Constrained Regular Triangulations by Flips", in //
// Proc. 19th Ann. Symp. on Comput. Geom., 86--95, 2003.                     //
//                                                                           //
// 'crosstets' contains the set of crossing tetrahedra (infected) of the     //
// facet.  'toppoints' and 'botpoints' are points lies above and below the   //
// facet, not on the facet.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints,
                                 arraypool *botpoints, arraypool *midpoints)
{
  arraypool *crossfaces, *bfacearray;
  triface fliptets[6], baktets[2], fliptet, newface;
  triface neightet, *parytet;
  face checksh;
  face checkseg;
  badface *pqueue;
  badface *popbf, bface;
  point plane_pa, plane_pb, plane_pc;
  point p1, p2, pd, pe;
  point *parypt;
  flipconstraints fc;
  REAL ori[3];
  int convcount, copcount;
  int flipflag, fcount;
  int n, i;
  long f23count, f32count, f44count;
  long totalfcount;

  f23count = flip23count;
  f32count = flip32count;
  f44count = flip44count;

  // Get three affinely independent vertices in the missing region R.
  calculateabovepoint(midpoints, &plane_pa, &plane_pb, &plane_pc);

  // Mark top and bottom points. Do not mark midpoints.
  for (i = 0; i < toppoints->objects; i++) {
    parypt = (point *) fastlookup(toppoints, i);
    if (!pmarktested(*parypt)) {
      pmarktest2(*parypt);
    }
  }
  for (i = 0; i < botpoints->objects; i++) {
    parypt = (point *) fastlookup(botpoints, i);
    if (!pmarktested(*parypt)) {
      pmarktest3(*parypt);
    }
  }

  // Collect crossing faces. 
  crossfaces = cavetetlist;  // Re-use array 'cavetetlist'.

  // Each crossing face contains at least one bottom vertex and
  //   one top vertex.
  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    fliptet = *parytet;
    for (fliptet.ver = 0; fliptet.ver < 4; fliptet.ver++) {
      fsym(fliptet, neightet);
      if (infected(neightet)) { // It is an interior face.
        if (!marktested(neightet)) { // It is an unprocessed face.
          crossfaces->newindex((void **) &parytet);
          *parytet = fliptet;
        }
      }
    }
    marktest(fliptet);
  }

  if (b->verbose > 1) {
    printf("    Found %ld crossing faces.\n", crossfaces->objects);
  }

  for (i = 0; i < crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    unmarktest(*parytet);
    uninfect(*parytet);
  }

  // Initialize the priority queue.
  pqueue = nullptr;

  for (i = 0; i < crossfaces->objects; i++) {
    parytet = (triface *) fastlookup(crossfaces, i);
    flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
  }
  crossfaces->restart();

  // The list for temporarily storing unflipable faces.
  bfacearray = new arraypool(sizeof(triface), 4);


  fcount = 0;  // Count the number of flips.

  // Flip insert the facet.
  while (pqueue != nullptr) {

    // Pop a face from the priotity queue.
    popbf = pqueue;
    bface = *popbf;

    // Update the queue.
    pqueue = pqueue->nextitem;

    // Delete the popped item from the pool.
    flippool->dealloc((void *) popbf);

    if (!isdeadtet(bface.tt)) {
      if ((org(bface.tt) == bface.forg) && (dest(bface.tt) == bface.fdest) &&
          (apex(bface.tt) == bface.fapex) && (oppo(bface.tt) == bface.foppo)) {
        // It is still a crossing face of R.
        fliptet = bface.tt;
        fsym(fliptet, neightet);
        assert(!isdeadtet(neightet));
        if (oppo(neightet) == bface.noppo) {
          pd = oppo(fliptet);
          pe = oppo(neightet);

          if (b->verbose > 2) {
            printf("      Get face (%d, %d, %d) - %d, %d, tau = %.17g\n",
                   pointmark(bface.forg), pointmark(bface.fdest),
                   pointmark(bface.fapex), pointmark(bface.foppo),
                   pointmark(bface.noppo), bface.key);
          }
          flipflag = 0;

          // Check for which type of flip can we do.
          convcount = 3;
          copcount = 0;
          for (i = 0; i < 3; i++) {
            p1 = org(fliptet);
            p2 = dest(fliptet);
            ori[i] = orient3d(p1, p2, pd, pe);
            if (ori[i] < 0) {
              convcount--;
              //break;
            } else if (ori[i] == 0) {
              convcount--; // Possible 4-to-4 flip.
              copcount++;
              //break;
            }
            enextself(fliptet);
          }

          if (convcount == 3) {
            // A 2-to-3 flip is found.
            // The face should not be a subface.
            tspivot(fliptet, checksh);
            assert(checksh.sh == nullptr);

            fliptets[0] = fliptet; // abcd, d may be the new vertex.
            fliptets[1] = neightet; // bace.
            flip23(fliptets, 1, &fc);
            // Put the link faces into check list.
            for (i = 0; i < 3; i++) {
              eprevesym(fliptets[i], newface);
              crossfaces->newindex((void **) &parytet);
              *parytet = newface;
            }
            for (i = 0; i < 3; i++) {
              enextesym(fliptets[i], newface);
              crossfaces->newindex((void **) &parytet);
              *parytet = newface;
            }
            flipflag = 1;
          } else if (convcount == 2) {
            assert(copcount <= 1);
            //if (copcount <= 1) {
            // A 3-to-2 or 4-to-4 may be possible.
            // Get the edge which is locally non-convex or flat. 
            for (i = 0; i < 3; i++) {
              if (ori[i] <= 0) break;
              enextself(fliptet);
            }
            // The edge should not be a segment.
            tsspivot1(fliptet, checkseg);
            assert(checkseg.sh == nullptr);

            // Collect tets sharing at this edge.
            // NOTE: This operation may collect tets which lie outside the
            //   cavity, e.g., when the edge lies on the boundary of the
            //   cavity. Do not flip if there are outside tets at this edge.
            //   2012-07-27.
            esym(fliptet, fliptets[0]); // [b,a,d,c]
            n = 0;
            do {
              p1 = apex(fliptets[n]);
              if (!(pmarktested(p1) || pmarktest2ed(p1) || pmarktest3ed(p1))) {
                // This apex is not on the cavity. Hence the face does not
                //   lie inside the cavity. Do not flip this edge.
                n = 1000; break;
              }
              fnext(fliptets[n], fliptets[n + 1]);
              n++;
            } while ((fliptets[n].tet != fliptet.tet) && (n < 5));

            if (n == 3) {
              // Found a 3-to-2 flip.
              flip32(fliptets, 1, &fc);
              // Put the link faces into check list.
              for (i = 0; i < 3; i++) {
                esym(fliptets[0], newface);
                crossfaces->newindex((void **) &parytet);
                *parytet = newface;
                enextself(fliptets[0]);
              }
              for (i = 0; i < 3; i++) {
                esym(fliptets[1], newface);
                crossfaces->newindex((void **) &parytet);
                *parytet = newface;
                enextself(fliptets[1]);
              }
              flipflag = 1;
            } else if (n == 4) {
              if (copcount == 1) {                
                // Found a 4-to-4 flip. 
                // Let the six vertices are: a,b,c,d,e,f, where
                //   fliptets[0] = [b,a,d,c]
                //           [1] = [b,a,c,e]
                //           [2] = [b,a,e,f]
                //           [3] = [b,a,f,d]
                // After the 4-to-4 flip, edge [a,b] is flipped, edge [e,d]
                //   is created.
                // First do a 2-to-3 flip.
                // Comment: This flip temporarily creates a degenerated
                //   tet (whose volume is zero). It will be removed by the 
                //   followed 3-to-2 flip.
                fliptets[0] = fliptet; // = [a,b,c,d], d is the new vertex.
                // fliptets[1];        // = [b,a,c,e].
                baktets[0] = fliptets[2]; // = [b,a,e,f]
                baktets[1] = fliptets[3]; // = [b,a,f,d]
                // The flip may involve hull tets.
                flip23(fliptets, 1, &fc);
                // Put the "outer" link faces into check list.
                //   fliptets[0] = [e,d,a,b] => will be flipped, so 
                //   [a,b,d] and [a,b,e] are not "outer" link faces.
                for (i = 1; i < 3; i++) {
                  eprevesym(fliptets[i], newface);
                  crossfaces->newindex((void **) &parytet);
                  *parytet = newface;
                }
                for (i = 1; i < 3; i++) {
                  enextesym(fliptets[i], newface);
                  crossfaces->newindex((void **) &parytet);
                  *parytet = newface;
                }
                // Then do a 3-to-2 flip.
                enextesymself(fliptets[0]);  // fliptets[0] is [e,d,a,b].
                eprevself(fliptets[0]); // = [b,a,d,c], d is the new vertex.
                fliptets[1] = baktets[0]; // = [b,a,e,f]
                fliptets[2] = baktets[1]; // = [b,a,f,d]
                flip32(fliptets, 1, &fc);
                // Put the "outer" link faces into check list.
                //   fliptets[0] = [d,e,f,a]
                //   fliptets[1] = [e,d,f,b]
                //   Faces [a,b,d] and [a,b,e] are not "outer" link faces.
                enextself(fliptets[0]);
                for (i = 1; i < 3; i++) {
                  esym(fliptets[0], newface);
                  crossfaces->newindex((void **) &parytet);
                  *parytet = newface;
                  enextself(fliptets[0]);
                }
                enextself(fliptets[1]);
                for (i = 1; i < 3; i++) {
                  esym(fliptets[1], newface);
                  crossfaces->newindex((void **) &parytet);
                  *parytet = newface;
                  enextself(fliptets[1]);
                }
                flip23count--;
                flip32count--;
                flip44count++;
                flipflag = 1;
              } else {
                //n == 4, convflag != 0; assert(0);
              }
            } else { 
              // n > 4 => unflipable. //assert(0);
            }
          } else {
            // There are more than 1 non-convex or coplanar cases.
            flipflag = -1; // Ignore this face.
            if (b->verbose > 2) {
              printf("        Ignore face (%d, %d, %d) - %d, %d, tau = %.17g\n",
                     pointmark(bface.forg), pointmark(bface.fdest),
                     pointmark(bface.fapex), pointmark(bface.foppo),
                     pointmark(bface.noppo), bface.key);
            }
          } // if (convcount == 1)

          if (flipflag == 1) {
            // Update the priority queue.
            for (i = 0; i < crossfaces->objects; i++) {
              parytet = (triface *) fastlookup(crossfaces, i);
              flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
            }
            crossfaces->restart();
            if (1) { // if (!b->flipinsert_random) {
              // Insert all queued unflipped faces.
              for (i = 0; i < bfacearray->objects; i++) {
                parytet = (triface *) fastlookup(bfacearray, i);
                // This face may be changed.
                if (!isdeadtet(*parytet)) {
                  flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
                }
              }
              bfacearray->restart();
            }
            fcount++;
          } else if (flipflag == 0) {
            // Queue an unflippable face. To process it later.
            bfacearray->newindex((void **) &parytet);
            *parytet = fliptet;
          }
        } // if (pe == bface.noppo)  
      } // if ((pa == bface.forg) && ...)
    } // if (bface.tt != nullptr)

  } // while (pqueue != nullptr)

  if (bfacearray->objects > 0) {
    if (fcount == 0) {
      printf("!! No flip is found in %ld faces.\n", bfacearray->objects);
      assert(0);
    }
  }

  // 'bfacearray' may be not empty (for what reason ??).
  //dbg_unflip_facecount += bfacearray->objects;

  assert(flippool->items == 0l);
  delete bfacearray;

  // Un-mark top and bottom points.
  for (i = 0; i < toppoints->objects; i++) {
    parypt = (point *) fastlookup(toppoints, i);
    punmarktest2(*parypt);
  }
  for (i = 0; i < botpoints->objects; i++) {
    parypt = (point *) fastlookup(botpoints, i);
    punmarktest3(*parypt);
  }

  f23count = flip23count - f23count;
  f32count = flip32count - f32count;
  f44count = flip44count - f44count;
  totalfcount = f23count + f32count + f44count;
  if (b->verbose > 2) {
    printf("      Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n",
           totalfcount, f23count, f32count, f44count);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// fillregion()    Fill the missing region by a set of new subfaces.         //
//                                                                           //
// 'missingshs' contains the list of subfaces in R.  Moreover, each subface  //
// (except the first one) in this list represents an interior edge of R.     //
//                                                                           //
// Note: We assume that all vertices of R are marktested so we can detect    //
// new subface by checking the flag in apexes.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds,
                            arraypool* newshs)
{
  badface *newflipface, *popface;
  triface searchtet, spintet, neightet;
  face oldsh, newsh, opensh, *parysh;
  face casout, casin, neighsh, checksh;
  face neighseg, checkseg;
  point pc;
  int success;
  int t1ver;
  int i, j;


  // Search the first new subface to fill the region.
  for (i = 0; i < missingshbds->objects; i++) {
    parysh = (face *) fastlookup(missingshbds, i);
    sspivot(*parysh, neighseg);
    sstpivot1(neighseg, searchtet);
    j = 0; // Count the number of passes of R.
    spintet = searchtet;
    while (1) {
      pc = apex(spintet);
      if (pmarktested(pc)) {
        neightet = spintet;
        j++;
      }
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break;
    }
    assert(j >= 1);
    if (j == 1) {
      // Found an interior new subface.
      searchtet = neightet;
      oldsh = *parysh;
      break;
    }
  } // i

  if (i == missingshbds->objects) {
    // Failed to find any interior subface.
    // Need Steiner points.
    return false;
  }

  makeshellface(subfaces, &newsh);
  setsorg(newsh, org(searchtet));
  setsdest(newsh, dest(searchtet));
  setsapex(newsh, apex(searchtet));
  // The new subface gets its markers from the old one.
  setshellmark(newsh, shellmark(oldsh));
  if (checkconstraints) {
    setareabound(newsh, areabound(oldsh));
  }
  // Connect the new subface to adjacent tets.
  tsbond(searchtet, newsh);
  fsymself(searchtet);
  sesymself(newsh);
  tsbond(searchtet, newsh);
  // Connect newsh to outer subfaces.
  sspivot(oldsh, checkseg);
  if (sinfected(checkseg)) {
    // It's a faked segment. Delete it.
    spintet = searchtet;
    while (1) {
      tssdissolve1(spintet);
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break;
    }
    shellfacedealloc(subsegs, checkseg.sh);
    ssdissolve(oldsh);
    checkseg.sh = nullptr;
  }
  spivot(oldsh, casout);
  if (casout.sh != nullptr) {
    casin = casout;
    if (checkseg.sh != nullptr) {
      // Make sure that the subface has the right ori at the segment.
      checkseg.shver = 0;
      if (sorg(newsh) != sorg(checkseg)) {
        sesymself(newsh);
      }
      spivot(casin, neighsh);
      while (neighsh.sh != oldsh.sh) {
        casin = neighsh;
        spivot(casin, neighsh);
      }
    }
    sbond1(newsh, casout);
    sbond1(casin, newsh);
  }
  if (checkseg.sh != nullptr) {
    ssbond(newsh, checkseg);
  }
  // Add this new subface into list.
  sinfect(newsh);
  newshs->newindex((void **) &parysh);
  *parysh = newsh;

  // Push two "open" side of the new subface into stack.
  for (i = 0; i < 2; i++) {
    senextself(newsh);
    newflipface = (badface *) flippool->alloc();
    newflipface->ss = newsh;
    newflipface->nextitem = flipstack;
    flipstack = newflipface;
  }

  success = 1;

  // Loop until 'flipstack' is empty.
  while ((flipstack != nullptr) && success) {
    // Pop an "open" side from the stack.
    popface = flipstack;
    opensh = popface->ss;
    flipstack = popface->nextitem; // The next top item in stack.
    flippool->dealloc((void *) popface);

    // opensh is either (1) an interior edge or (2) a bdry edge.
    stpivot(opensh, searchtet);
    tsspivot1(searchtet, checkseg);
    if (checkseg.sh == nullptr) {
      // No segment. It is an interior edge of R. 
      // Search for a new face in R.
      spintet = searchtet;
      fnextself(spintet); // Skip the current face.
      while (1) {
        pc = apex(spintet);
        if (pmarktested(pc)) {
          // 'opensh' is an interior edge.
          if (!issubface(spintet)) {
            // Create a new subface.
            makeshellface(subfaces, &newsh);
            setsorg(newsh, org(spintet));
            setsdest(newsh, dest(spintet));
            setsapex(newsh, pc);
            // The new subface gets its markers from its neighbor.
            setshellmark(newsh, shellmark(opensh));
            if (checkconstraints) {
              setareabound(newsh, areabound(opensh));
            }
            // Connect the new subface to adjacent tets.
            tsbond(spintet, newsh);
            fsymself(spintet);
            sesymself(newsh);
            tsbond(spintet, newsh);
            // Connect newsh to its adjacent subface.
            sbond(newsh, opensh);
            // Add this new subface into list.
            sinfect(newsh);
            newshs->newindex((void **) &parysh);
            *parysh = newsh;
            // Push two "open" side of the new subface into stack.
            for (i = 0; i < 2; i++) {
              senextself(newsh);
              newflipface = (badface *) flippool->alloc();
              newflipface->ss = newsh;
              newflipface->nextitem = flipstack;
              flipstack = newflipface;
            }
          } else {
            // Connect to another open edge.
            tspivot(spintet, checksh);
            sbond(opensh, checksh); 
          }
          break;
        } // if (pmarktested(pc))
        fnextself(spintet);
        if (spintet.tet == searchtet.tet) {
          // Not find any face to fill in R at this side.
          // Suggest a point to split the edge.
          success = 0;
          break;
        }
      } // while (1)
    } else {
      // This side coincident with a boundary edge of R.
      checkseg.shver = 0;
      spivot(checkseg, oldsh);
      if (sinfected(checkseg)) {
        // It's a faked segment. Delete it.
        spintet = searchtet;
        while (1) {
          tssdissolve1(spintet);
          fnextself(spintet);
          if (spintet.tet == searchtet.tet) break;
        }
        shellfacedealloc(subsegs, checkseg.sh);
        ssdissolve(oldsh);
        checkseg.sh = nullptr;
      }
      spivot(oldsh, casout);
      if (casout.sh != nullptr) {
        casin = casout;
        if (checkseg.sh != nullptr) {
          // Make sure that the subface has the right ori at the segment.
          checkseg.shver = 0;
          if (sorg(opensh) != sorg(checkseg)) {
            sesymself(opensh);
              }
          spivot(casin, neighsh);
          while (neighsh.sh != oldsh.sh) {
            casin = neighsh;
            spivot(casin, neighsh);
          }
        }
        sbond1(opensh, casout);
        sbond1(casin, opensh);
      }
      if (checkseg.sh != nullptr) {
        ssbond(opensh, checkseg);
      }
    } // if (checkseg.sh != nullptr)
  } // while ((flipstack != nullptr) && success)

  if (success) {
    // Uninfect all new subfaces.
    for (i = 0; i < newshs->objects; i++) {
      parysh = (face *) fastlookup(newshs, i);
      suninfect(*parysh);
    }
    // Delete old subfaces.
    for (i = 0; i < missingshs->objects; i++) {
      parysh = (face *) fastlookup(missingshs, i);
      shellfacedealloc(subfaces, parysh->sh);
    }
    fillregioncount++;
  } else {
    // Failed to fill the region. 
    // Re-connect old subfaces at boundaries of R.
    // Also delete fake segments.
    for (i = 0; i < missingshbds->objects; i++) {
      parysh = (face *) fastlookup(missingshbds, i);
      // It still connect to 'casout'. 
      // Re-connect 'casin' to it.
      spivot(*parysh, casout);
      casin = casout;
      spivot(casin, neighsh);
      while (1) {
        if (sinfected(neighsh)) break;
        if (neighsh.sh == parysh->sh) break;
        casin = neighsh;
        spivot(casin, neighsh);
      }
      if (sinfected(neighsh)) {
        sbond1(casin, *parysh);
      }
      sspivot(*parysh, checkseg);
      if (checkseg.sh != nullptr) {
        if (checkseg.sh[3] != nullptr) {
          if (sinfected(checkseg)) {
            sstpivot1(checkseg, searchtet);
            spintet = searchtet;
            while (1) {
              tssdissolve1(spintet);
              fnextself(spintet);
              if (spintet.tet == searchtet.tet) break;
            }
            ssdissolve(*parysh);
            shellfacedealloc(subsegs, checkseg.sh);
          }
        }
      }
    }
    // Delete all new subfaces.
    for (i = 0; i < newshs->objects; i++) {
      parysh = (face *) fastlookup(newshs, i);
      shellfacedealloc(subfaces, parysh->sh);
    }
    // Clear the flip pool.    
    flippool->restart();
    flipstack = nullptr;

    // Choose an interior edge of R to split.
    assert(missingshs->objects > 1);
    // Skip the first subface in 'missingshs'.
    i = randomnation(missingshs->objects - 1) + 1;
    parysh = (face *) fastlookup(missingshs, i);
    recentsh = *parysh;
  }

  newshs->restart();

  return (success!=0);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertpoint_cdt()    Insert a new point into a CDT.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, 
                                face *splitseg, insertvertexflags *ivf,
                                arraypool *cavpoints, arraypool *cavfaces,
                                arraypool *cavshells, arraypool *newtets,
                                arraypool *crosstets, arraypool *misfaces)
{
  triface neightet, *parytet;
  face checksh, *parysh, *parysh1;
  face *paryseg, *paryseg1;
  point *parypt;
  int t1ver;
  int i;

  if (b->verbose > 2) {
    printf("      Insert point %d into CDT\n", pointmark(newpt));
  }

  if (!insertpoint(newpt, searchtet, nullptr, nullptr, ivf)) {
    // Point is not inserted. Check ivf->iloc for reason.
    return 0;
  }


  for (i = 0; i < cavetetvertlist->objects; i++) {
    cavpoints->newindex((void **) &parypt);
    *parypt = * (point *) fastlookup(cavetetvertlist, i);
  }
  // Add the new point into the point list.
  cavpoints->newindex((void **) &parypt);
  *parypt = newpt;

  for (i = 0; i < cavebdrylist->objects; i++) {
    cavfaces->newindex((void **) &parytet);
    *parytet = * (triface *) fastlookup(cavebdrylist, i);
  }

  for (i = 0; i < caveoldtetlist->objects; i++) {
    crosstets->newindex((void **) &parytet);
    *parytet = * (triface *) fastlookup(caveoldtetlist, i);
  }

  cavetetvertlist->restart();
  cavebdrylist->restart();
  caveoldtetlist->restart();

  // Insert the point using the cavity algorithm.
  delaunizecavity(cavpoints, cavfaces, cavshells, newtets, crosstets, 
                  misfaces);
  fillcavity(cavshells, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
  carvecavity(crosstets, newtets, nullptr);

  if ((splitsh != nullptr) || (splitseg != nullptr)) {
    // Insert the point into the surface mesh.
    sinsertvertex(newpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0);

    // Put all new subfaces into stack.
    for (i = 0; i < caveshbdlist->objects; i++) { 
      // Get an old subface at edge [a, b].
      parysh = (face *) fastlookup(caveshbdlist, i);
      spivot(*parysh, checksh); // The new subface [a, b, p].
      // Do not recover a deleted new face (degenerated).
      if (checksh.sh[3] != nullptr) {
        subfacstack->newindex((void **) &parysh);
        *parysh = checksh;
      }
    }

    if (splitseg != nullptr) {
      // Queue two new subsegments in C(p) for recovery.
      for (i = 0; i < cavesegshlist->objects; i++) {
        paryseg = (face *) fastlookup(cavesegshlist, i);
        subsegstack->newindex((void **) &paryseg1);
        *paryseg1 = *paryseg;
      }
    } // if (splitseg != nullptr)

    // Delete the old subfaces in sC(p).
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      if (checksubfaceflag) {
        // It is possible that this subface still connects to adjacent
        //   tets which are not in C(p). If so, clear connections in the
        //   adjacent tets at this subface.
        stpivot(*parysh, neightet);
        if (neightet.tet != nullptr) {
          if (neightet.tet[4] != nullptr) {
            // Found an adjacent tet. It must be not in C(p).
            assert(!infected(neightet));
            tsdissolve(neightet);
            fsymself(neightet);
            assert(!infected(neightet));
            tsdissolve(neightet);
          }
        }
      }
      shellfacedealloc(subfaces, parysh->sh);
    }
    if (splitseg != nullptr) {
      // Delete the old segment in sC(p).
      shellfacedealloc(subsegs, splitseg->sh);
    }

    // Clear working lists.
    caveshlist->restart();
    caveshbdlist->restart();
    cavesegshlist->restart();
  } // if ((splitsh != nullptr) || (splitseg != nullptr)) 

  // Put all interior subfaces into stack for recovery.
  // They were collected in carvecavity().
  // Note: Some collected subfaces may be deleted by sinsertvertex().
  for (i = 0; i < caveencshlist->objects; i++) {
    parysh = (face *) fastlookup(caveencshlist, i);
    if (parysh->sh[3] != nullptr) {
      subfacstack->newindex((void **) &parysh1);
      *parysh1 = *parysh;
    }
  }

  // Put all interior segments into stack for recovery.
  // They were collected in carvecavity().
  // Note: Some collected segments may be deleted by sinsertvertex().
  for (i = 0; i < caveencseglist->objects; i++) {
    paryseg = (face *) fastlookup(caveencseglist, i);
    if (paryseg->sh[3] != nullptr) {
      subsegstack->newindex((void **) &paryseg1);
      *paryseg1 = *paryseg;
    }
  }

  caveencshlist->restart();
  caveencseglist->restart();

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// refineregion()    Refine a missing region by inserting points.            //
//                                                                           //
// 'splitsh' represents an edge of the facet to be split. It must be not a   //
// segment. 
//                                                                           //
// Assumption: The current mesh is a CDT and is convex.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, 
                              arraypool *cavfaces, arraypool *cavshells,
                              arraypool *newtets, arraypool *crosstets,
                              arraypool *misfaces)
{
  triface searchtet, spintet;
  face splitseg, *paryseg;
  point steinpt, pa, pb, refpt;
  insertvertexflags ivf;
  enum interresult dir;
  long baknum = points->items;
  int t1ver;
  int i;

  if (b->verbose > 2) {
    printf("      Refining region at edge (%d, %d, %d).\n",
           pointmark(sorg(splitsh)), pointmark(sdest(splitsh)),
           pointmark(sapex(splitsh)));
  }

  // Add the Steiner point at the barycenter of the face.
  pa = sorg(splitsh);
  pb = sdest(splitsh);
  // Create a new point.
  makepoint(&steinpt, FREEFACETVERTEX);
  for (i = 0; i < 3; i++) {
    steinpt[i] = 0.5 * (pa[i] + pb[i]);
  }

  ivf.bowywat = 1; // Use the Bowyer-Watson algorrithm.
  ivf.cdtflag = 1; // Only create the initial cavity.
  ivf.sloc = (int) ONEDGE;
  ivf.sbowywat = 1;
  ivf.assignmeshsize = b->metric;

  point2tetorg(pa, searchtet); // Start location from it.
  ivf.iloc = (int) OUTSIDE;

  ivf.rejflag = 1; // Reject it if it encroaches upon any segment.
  if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, nullptr, &ivf, cavpoints,
                       cavfaces, cavshells, newtets, crosstets, misfaces)) {
    if (ivf.iloc == (int) ENCSEGMENT) {
      pointdealloc(steinpt);
      // Split an encroached segment.
      assert(encseglist->objects > 0);
      i = randomnation(encseglist->objects);
      paryseg = (face *) fastlookup(encseglist, i);
      splitseg = *paryseg;
      encseglist->restart();

      // Split the segment.
      pa = sorg(splitseg);
      pb = sdest(splitseg);
      // Create a new point.
      makepoint(&steinpt, FREESEGVERTEX);
      for (i = 0; i < 3; i++) {
        steinpt[i] = 0.5 * (pa[i] + pb[i]);
      }
      point2tetorg(pa, searchtet);
      ivf.iloc = (int) OUTSIDE;
      ivf.rejflag = 0;
      if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf,
                           cavpoints, cavfaces, cavshells, newtets, 
                           crosstets, misfaces)) {
        assert(0);
      }
      st_segref_count++;
      if (steinerleft > 0) steinerleft--;
    } else {
      assert(0);
    }
  } else {
    st_facref_count++;
    if (steinerleft > 0) steinerleft--;
  }

  while (subsegstack->objects > 0l) {
    // seglist is used as a stack.
    subsegstack->objects--;
    paryseg = (face *) fastlookup(subsegstack, subsegstack->objects);
    splitseg = *paryseg;

    // Check if this segment has been recovered.
    sstpivot1(splitseg, searchtet);
    if (searchtet.tet != nullptr) continue;

    // Search the segment.
    dir = scoutsegment(sorg(splitseg), sdest(splitseg), &searchtet, &refpt, 
                       nullptr);
    if (dir == SHAREEDGE) {
      // Found this segment, insert it.
      if (!issubseg(searchtet)) {
        // Let the segment remember an adjacent tet.
        sstbond1(splitseg, searchtet);
        // Bond the segment to all tets containing it.
        spintet = searchtet;
        do {
          tssbond1(spintet, splitseg);
          fnextself(spintet);
        } while (spintet.tet != searchtet.tet);
      } else {
        // Collision! Should not happen.
        assert(0);
      }
    } else { 
      if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
        // Split the segment.
        // Create a new point.
        makepoint(&steinpt, FREESEGVERTEX);
        //setpointtype(newpt, FREESEGVERTEX);
        getsteinerptonsegment(&splitseg, refpt, steinpt);
        ivf.iloc = (int) OUTSIDE;
        ivf.rejflag = 0;
        if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf,
                             cavpoints, cavfaces, cavshells, newtets, 
                             crosstets, misfaces)) {
          assert(0);
        }
        st_segref_count++;
        if (steinerleft > 0) steinerleft--;
      } else {
        // Maybe a PLC problem.
        assert(0);
      }
    }
  } // while

  if (b->verbose > 2) {
    printf("      Added %ld Steiner points.\n", points->items - baknum);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedfacets()    Recover constrained facets in a CDT.               //
//                                                                           //
// All unrecovered subfaces are queued in 'subfacestack'.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constrainedfacets()
{
  arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets;
  arraypool *tg_topfaces, *tg_botfaces, *tg_midfaces;
  arraypool *tg_topshells, *tg_botshells, *tg_facfaces; 
  arraypool *tg_toppoints, *tg_botpoints;
  arraypool *tg_missingshs, *tg_missingshbds, *tg_missingshverts;
  triface searchtet, neightet, crossedge;
  face searchsh, *parysh, *parysh1;
  face *paryseg;
  point *parypt;
  enum interresult dir;
  int facetcount;
  int success;
  int t1ver;
  int i, j;

  // Initialize arrays.
  tg_crosstets      = new arraypool(sizeof(triface), 10);
  tg_topnewtets     = new arraypool(sizeof(triface), 10);
  tg_botnewtets     = new arraypool(sizeof(triface), 10);
  tg_topfaces       = new arraypool(sizeof(triface), 10);
  tg_botfaces       = new arraypool(sizeof(triface), 10);
  tg_midfaces       = new arraypool(sizeof(triface), 10);
  tg_toppoints      = new arraypool(sizeof(point), 8);
  tg_botpoints      = new arraypool(sizeof(point), 8);
  tg_facfaces       = new arraypool(sizeof(face), 10);
  tg_topshells      = new arraypool(sizeof(face), 10);
  tg_botshells      = new arraypool(sizeof(face), 10);
  tg_missingshs     = new arraypool(sizeof(face), 10);
  tg_missingshbds   = new arraypool(sizeof(face), 10);
  tg_missingshverts = new arraypool(sizeof(point), 8);
  // This is a global array used by refineregion().
  encseglist        = new arraypool(sizeof(face), 4);

  facetcount = 0;

  while (subfacstack->objects > 0l) {

    subfacstack->objects--;
    parysh = (face *) fastlookup(subfacstack, subfacstack->objects);
    searchsh = *parysh;

    if (searchsh.sh[3] == nullptr) continue; // It is dead.
    if (isshtet(searchsh)) continue; // It is recovered.

    // Collect all unrecovered subfaces which are co-facet.
    smarktest(searchsh);
    tg_facfaces->newindex((void **) &parysh);
    *parysh = searchsh;
    for (i = 0; i < tg_facfaces->objects; i++) {
      parysh = (face *) fastlookup(tg_facfaces, i);
      for (j = 0; j < 3; j++) {
        if (!isshsubseg(*parysh)) {
          spivot(*parysh, searchsh);
          assert(searchsh.sh != nullptr); // SELF_CHECK
          if (!smarktested(searchsh)) {
            if (!isshtet(searchsh)) {
              smarktest(searchsh);
              tg_facfaces->newindex((void **) &parysh1);
              *parysh1 = searchsh;
            }
          }
        }
        senextself(*parysh);
      } // j
    } // i
    // Have found all facet subfaces. Unmark them.
    for (i = 0; i < tg_facfaces->objects; i++) {
      parysh = (face *) fastlookup(tg_facfaces, i);
      sunmarktest(*parysh);
    }

    if (b->verbose > 2) {
      printf("    Recovering facet #%d: %ld subfaces.\n", facetcount + 1, 
             tg_facfaces->objects);
    }
    facetcount++;

    while (tg_facfaces->objects > 0l) {

      tg_facfaces->objects--;
      parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects);
      searchsh = *parysh;

      if (searchsh.sh[3] == nullptr) continue; // It is dead.
      if (isshtet(searchsh)) continue; // It is recovered.

      searchtet.tet = nullptr;
      dir = scoutsubface(&searchsh, &searchtet);
      if (dir == SHAREFACE) continue; // The subface is inserted.

      // The subface is missing. Form the missing region.
      //   Re-use 'tg_crosstets' for 'adjtets'.
      formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts);

      if (scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs)) {
        // Save this crossing edge, will be used by fillcavity().
        crossedge = searchtet;
        // Form a cavity of crossing tets.
        success = formcavity(&searchtet, tg_missingshs, tg_crosstets,
                             tg_topfaces, tg_botfaces, tg_toppoints,
                             tg_botpoints);
        if (success) {
          if (!b->flipinsert) {
            // Tetrahedralize the top part. Re-use 'tg_midfaces'.
            delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells,
                            tg_topnewtets, tg_crosstets, tg_midfaces);
            // Tetrahedralize the bottom part. Re-use 'tg_midfaces'.
            delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells,
                            tg_botnewtets, tg_crosstets, tg_midfaces);
            // Fill the cavity with new tets.
            success = fillcavity(tg_topshells, tg_botshells, tg_midfaces,
                                 tg_missingshs, tg_topnewtets, tg_botnewtets,
                                 &crossedge);
            if (success) {
              // Cavity is remeshed. Delete old tets and outer new tets.
              carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets);
            } else {
              restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets,
                            tg_missingshbds);
            }
          } else {
            // Use the flip algorithm of Shewchuk to recover the subfaces.
            flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, 
                            tg_missingshverts);
            // Recover the missing region.
            success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells);
            assert(success);
            // Clear working lists.
            tg_crosstets->restart();
            tg_topfaces->restart();
            tg_botfaces->restart();
            tg_toppoints->restart();
            tg_botpoints->restart();
          } // b->flipinsert

          if (success) {
            // Recover interior subfaces.
            for (i = 0; i < caveencshlist->objects; i++) {
              parysh = (face *) fastlookup(caveencshlist, i);
              dir = scoutsubface(parysh, &searchtet);
              if (dir != SHAREFACE) {
                // Add this face at the end of the list, so it will be
                //   processed immediately.
                tg_facfaces->newindex((void **) &parysh1);
                *parysh1 = *parysh;
              }
            }
            caveencshlist->restart();
            // Recover interior segments. This should always be recovered.
            for (i = 0; i < caveencseglist->objects; i++) {
              paryseg = (face *) fastlookup(caveencseglist, i);
              dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet,
                                 nullptr, nullptr);
              assert(dir == SHAREEDGE);
              // Insert this segment.
              if (!issubseg(searchtet)) {
                // Let the segment remember an adjacent tet.
                sstbond1(*paryseg, searchtet);
                // Bond the segment to all tets containing it.
                neightet = searchtet;
                do {
                  tssbond1(neightet, *paryseg);
                  fnextself(neightet);
                } while (neightet.tet != searchtet.tet);
              } else {
                // Collision! Should not happen.
                assert(0);
              }
            }
            caveencseglist->restart();
          } // success - remesh cavity
        } // success - form cavity
      } else {
        // Recover subfaces by retriangulate the surface mesh.
        //   Re-use tg_topshells for newshs.
        success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells);
      }

      // Unmarktest all points of the missing region.
      for (i = 0; i < tg_missingshverts->objects; i++) {
        parypt = (point *) fastlookup(tg_missingshverts, i);
        punmarktest(*parypt);
      }
      tg_missingshverts->restart();
      tg_missingshbds->restart();
      tg_missingshs->restart();

      if (!success) {
        // The missing region can not be recovered. Refine it.
        refineregion(recentsh, tg_toppoints, tg_topfaces, tg_topshells,
                     tg_topnewtets, tg_crosstets, tg_midfaces);
        // Clean the current list of facet subfaces.
        // tg_facfaces->restart();
      }
    } // while (tg_facfaces->objects)

  } // while ((subfacstack->objects)

  // Accumulate the dynamic memory.
  totalworkmemory += (tg_crosstets->totalmemory + tg_topnewtets->totalmemory +
                      tg_botnewtets->totalmemory + tg_topfaces->totalmemory +
                      tg_botfaces->totalmemory + tg_midfaces->totalmemory +
                      tg_toppoints->totalmemory + tg_botpoints->totalmemory +
                      tg_facfaces->totalmemory + tg_topshells->totalmemory +
                      tg_botshells->totalmemory + tg_missingshs->totalmemory +
                      tg_missingshbds->totalmemory + 
                      tg_missingshverts->totalmemory + 
                      encseglist->totalmemory);

  // Delete arrays.
  delete tg_crosstets;
  delete tg_topnewtets;
  delete tg_botnewtets;
  delete tg_topfaces;
  delete tg_botfaces;
  delete tg_midfaces;
  delete tg_toppoints;
  delete tg_botpoints;
  delete tg_facfaces;
  delete tg_topshells;
  delete tg_botshells;
  delete tg_missingshs;
  delete tg_missingshbds;
  delete tg_missingshverts;
  delete encseglist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constraineddelaunay()    Create a constrained Delaunay tetrahedralization.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constraineddelaunay(clock_t& tv)
{
  face searchsh, *parysh;
  face searchseg, *paryseg;
  int s, i;

  // Statistics.
  long bakfillregioncount;
  long bakcavitycount, bakcavityexpcount;
  long bakseg_ref_count;

  if (!b->quiet) {
    printf("Constrained Delaunay...\n");
  }

  // Identify acute vertex for PLC inputs.
  markacutevertices();

  if (b->verbose) {
    printf("  Delaunizing segments.\n");
  }

  checksubsegflag = 1;

  // Put all segments into the list (in random order).
  subsegs->traversalinit();
  for (i = 0; i < subsegs->items; i++) {
    s = randomnation(i + 1);
    // Move the s-th seg to the i-th.
    subsegstack->newindex((void **) &paryseg);
    *paryseg = * (face *) fastlookup(subsegstack, s);
    // Put i-th seg to be the s-th.
    searchseg.sh = shellfacetraverse(subsegs);
    //sinfect(searchseg);  // Only save it once.
    paryseg = (face *) fastlookup(subsegstack, s);
    *paryseg = searchseg;
  }

  // Recover non-Delaunay segments.
  delaunizesegments();

  if (b->verbose) {
    printf("  Inserted %ld Steiner points.\n", st_segref_count); 
  }

  tv = clock();

  if (b->verbose) {
    printf("  Constraining facets.\n");
  }

  // Subfaces will be introduced.
  checksubfaceflag = 1;

  bakfillregioncount = fillregioncount;
  bakcavitycount = cavitycount;
  bakcavityexpcount = cavityexpcount;
  bakseg_ref_count = st_segref_count;

  // Randomly order the subfaces.
  subfaces->traversalinit();
  for (i = 0; i < subfaces->items; i++) {
    s = randomnation(i + 1);
    // Move the s-th subface to the i-th.
    subfacstack->newindex((void **) &parysh);
    *parysh = * (face *) fastlookup(subfacstack, s);
    // Put i-th subface to be the s-th.
    searchsh.sh = shellfacetraverse(subfaces);
    parysh = (face *) fastlookup(subfacstack, s);
    *parysh = searchsh;
  }

  // Recover facets.
  constrainedfacets();

  if (b->verbose) {
    if (fillregioncount > bakfillregioncount) {
      printf("  Remeshed %ld regions.\n", fillregioncount-bakfillregioncount);
    }
    if (cavitycount > bakcavitycount) {
      printf("  Remeshed %ld cavities", cavitycount - bakcavitycount);
      if (cavityexpcount - bakcavityexpcount) {
        printf(" (%ld enlarged)", cavityexpcount - bakcavityexpcount);
      }
      printf(".\n");
    }
    if (st_segref_count + st_facref_count - bakseg_ref_count > 0) {
      printf("  Inserted %ld (%ld, %ld) refine points.\n", 
             st_segref_count + st_facref_count - bakseg_ref_count,
             st_segref_count - bakseg_ref_count, st_facref_count);
    }
  }
}

////                                                                       ////
////                                                                       ////
//// constrained_cxx //////////////////////////////////////////////////////////

//// steiner_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkflipeligibility()    A call back function for boundary recovery.     //
//                                                                           //
// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, //
// and 2 : 3-to-2, respectively.                                             //
//                                                                           //
// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is    //
// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',//
// other points must not be 'dummypoint'.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, 
                                     point pc, point pd, point pe,
                                     int level, int edgepivot,
                                     flipconstraints* fc)
{
  point tmppts[3];
  enum interresult dir;
  int types[2], poss[4];
  int intflag;
  int rejflag = 0;
  int i;

  if (fc->seg[0] != nullptr) {
    // A constraining edge is given (e.g., for edge recovery).
    if (fliptype == 1) {
      // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
      tmppts[0] = pa;
      tmppts[1] = pb;
      tmppts[2] = pc;
      for (i = 0; i < 3 && !rejflag; i++) {
        if (tmppts[i] != dummypoint) {
          // Test if the face [e,d,#] intersects the edge.
          intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], 
                                  nullptr, 1, types, poss);
          if (intflag == 2) {
            // They intersect at a single point.
            dir = (enum interresult) types[0];
            if (dir == ACROSSFACE) {
              // The interior of [e,d,#] intersect the segment.
              rejflag = 1;
            } else if (dir == ACROSSEDGE) {
              if (poss[0] == 0) {
                // The interior of [e,d] intersect the segment.
                // Since [e,d] is the newly created edge. Reject this flip.
                rejflag = 1; 
              }
            }
          } else if (intflag == 4) {
            // They may intersect at either a point or a line segment.
            dir = (enum interresult) types[0];
            if (dir == ACROSSEDGE) {
              if (poss[0] == 0) {
                // The interior of [e,d] intersect the segment.
                // Since [e,d] is the newly created edge. Reject this flip.
                rejflag = 1;
              }
            }
          }
        } // if (tmppts[0] != dummypoint)
      } // i
    } else if (fliptype == 2) {
      // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
      if (pc != dummypoint) {
        // Check if the new face [a,b,c] intersect the edge in its interior.
        intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], nullptr, 
                                1, types, poss);
        if (intflag == 2) {
          // They intersect at a single point.
          dir = (enum interresult) types[0];
          if (dir == ACROSSFACE) {
            // The interior of [a,b,c] intersect the segment.
            rejflag = 1; // Do not flip.
          }
        } else if (intflag == 4) {
          // [a,b,c] is coplanar with the edge. 
          dir = (enum interresult) types[0];
          if (dir == ACROSSEDGE) {
            // The boundary of [a,b,c] intersect the segment.            
            rejflag = 1; // Do not flip.
          }
        }
      } // if (pc != dummypoint)
    }
  } // if (fc->seg[0] != nullptr)

  if ((fc->fac[0] != nullptr) && !rejflag) {
    // A constraining face is given (e.g., for face recovery).
    if (fliptype == 1) {
      // A 2-to-3 flip.
      // Test if the new edge [e,d] intersects the face.
      intflag = tri_edge_test(fc->fac[0], fc->fac[1], fc->fac[2], pe, pd, 
                              nullptr, 1, types, poss);
      if (intflag == 2) {
        // They intersect at a single point.
        dir = (enum interresult) types[0];
        if (dir == ACROSSFACE) {
          rejflag = 1;
        } else if (dir == ACROSSEDGE) {
          rejflag = 1;
        } 
      } else if (intflag == 4) {
        // The edge [e,d] is coplanar with the face.
        // There may be two intersections.
        for (i = 0; i < 2 && !rejflag; i++) {
          dir = (enum interresult) types[i];
          if (dir == ACROSSFACE) {
            rejflag = 1;
          } else if (dir == ACROSSEDGE) {
            rejflag = 1;
          }
        }
      }
    } // if (fliptype == 1)
  } // if (fc->fac[0] != nullptr)

  if ((fc->remvert != nullptr) && !rejflag) {
    // The vertex is going to be removed. Do not create a new edge which
    //   contains this vertex.
    if (fliptype == 1) {
      // A 2-to-3 flip.
      if ((pd == fc->remvert) || (pe == fc->remvert)) {
        rejflag = 1;
      }
    }
  }

  if (fc->remove_large_angle && !rejflag) {
    // Remove a large dihedral angle. Do not create a new small angle.
    REAL cosmaxd = 0, diff;
    if (fliptype == 1) {
      // We assume that neither 'a' nor 'b' is dummypoint.
      assert((pa != dummypoint) && (pb != dummypoint)); // SELF_CHECK
      // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
      // The new tet [e,d,a,b] will be flipped later. Only two new tets:
      //   [e,d,b,c] and [e,d,c,a] need to be checked.
      if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) {
        // Get the largest dihedral angle of [e,d,b,c].
        tetalldihedral(pe, pd, pb, pc, nullptr, &cosmaxd, nullptr);
        diff = cosmaxd - fc->cosdihed_in;
        if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
        if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
          rejflag = 1;
        } else {
          // Record the largest new angle.
          if (cosmaxd < fc->cosdihed_out) {
            fc->cosdihed_out = cosmaxd; 
          }
          // Get the largest dihedral angle of [e,d,c,a].
          tetalldihedral(pe, pd, pc, pa, nullptr, &cosmaxd, nullptr);
          diff = cosmaxd - fc->cosdihed_in;
          if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
            rejflag = 1;
          } else {
            // Record the largest new angle.
            if (cosmaxd < fc->cosdihed_out) {
              fc->cosdihed_out = cosmaxd; 
            }
          }
        }
      } // if (pc != dummypoint && ...)
    } else if (fliptype == 2) {
      // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
      // We assume that neither 'e' nor 'd' is dummypoint.
      assert((pe != dummypoint) && (pd != dummypoint)); // SELF_CHECK
      if (level == 0) {
        // Both new tets [a,b,c,d] and [b,a,c,e] are new tets.
        if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
          // Get the largest dihedral angle of [a,b,c,d].
          tetalldihedral(pa, pb, pc, pd, nullptr, &cosmaxd, nullptr);
          diff = cosmaxd - fc->cosdihed_in;
          if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding
          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
            rejflag = 1;
          } else {
            // Record the largest new angle.
            if (cosmaxd < fc->cosdihed_out) {
              fc->cosdihed_out = cosmaxd; 
            }
            // Get the largest dihedral angle of [b,a,c,e].
            tetalldihedral(pb, pa, pc, pe, nullptr, &cosmaxd, nullptr);
            diff = cosmaxd - fc->cosdihed_in;
            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              if (cosmaxd < fc->cosdihed_out) {
                fc->cosdihed_out = cosmaxd; 
              }
            }
          }
        }
      } else { // level > 0
        assert(edgepivot != 0);
        if (edgepivot == 1) {
          // The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e].
          if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
            // Get the largest dihedral angle of [b,a,c,e].
            tetalldihedral(pb, pa, pc, pe, nullptr, &cosmaxd, nullptr);
            diff = cosmaxd - fc->cosdihed_in;
            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              if (cosmaxd < fc->cosdihed_out) {
                fc->cosdihed_out = cosmaxd; 
              }
            }
          }
        } else {
          assert(edgepivot == 2);
          // The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d].
          if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) {
            // Get the largest dihedral angle of [b,a,c,e].
            tetalldihedral(pa, pb, pc, pd, nullptr, &cosmaxd, nullptr);
            diff = cosmaxd - fc->cosdihed_in;
            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              if (cosmaxd < fc->cosdihed_out) {
                fc->cosdihed_out = cosmaxd; 
              }
            }
          }
        } // edgepivot
      } // level
    }
  }

  return rejflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflips()    Remove an edge by flips.                           //
//                                                                           //
// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed.          //
//                                                                           //
// The return value is a positive integer, it indicates whether the edge is  //
// removed or not.  A value "2" means the edge is removed, othereise, the    //
// edge is not removed and the value (must >= 3) is the current number of    //
// tets in the edge star.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc)
{
  triface *abtets, spintet;
  int t1ver; 
  int n, nn, i;


  if (checksubsegflag) {
    // Do not flip a segment.
    if (issubseg(*flipedge)) {
      if (fc->collectencsegflag) {
        face checkseg, *paryseg;
        tsspivot1(*flipedge, checkseg);
        if (!sinfected(checkseg)) {
          // Queue this segment in list.
          sinfect(checkseg);                
          caveencseglist->newindex((void **) &paryseg);
          *paryseg = checkseg;
        }
      }
      return 0;
    }
  }

  // Count the number of tets at edge [a,b].
  n = 0;
  spintet = *flipedge;
  while (1) {
    n++;
    fnextself(spintet);
    if (spintet.tet == flipedge->tet) break;
  }
  assert(n >= 3);

  if ((b->flipstarsize > 0) && (n > b->flipstarsize)) {
    // The star size exceeds the limit.
    return 0; // Do not flip it.
  }

  // Allocate spaces.
  abtets = new triface[n];
  // Collect the tets at edge [a,b].
  spintet = *flipedge;
  i = 0;
  while (1) {
    abtets[i] = spintet;
    setelemcounter(abtets[i], 1); 
    i++;
    fnextself(spintet);
    if (spintet.tet == flipedge->tet) break;
  }


  // Try to flip the edge (level = 0, edgepivot = 0).
  nn = flipnm(abtets, n, 0, 0, fc);


  if (nn > 2) {
    // Edge is not flipped. Unmarktest the remaining tets in Star(ab).
    for (i = 0; i < nn; i++) {
      setelemcounter(abtets[i], 0);
    }
    // Restore the input edge (needed by Lawson's flip).
    *flipedge = abtets[0];
  }

  // Release the temporary allocated spaces.
  // NOTE: fc->unflip must be 0.
  int bakunflip = fc->unflip;
  fc->unflip = 0;
  flipnm_post(abtets, n, nn, 0, fc);
  fc->unflip = bakunflip;

  delete [] abtets;

  return nn; 
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removefacebyflips()    Remove a face by flips.                            //
//                                                                           //
// Return 1 if the face is removed. Otherwise, return 0.                     //
//                                                                           //
// ASSUMPTIONS:                                                              //
//   - 'flipface' must not be a hull face.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc)
{
  if (checksubfaceflag) {
    if (issubface(*flipface)) {
      return 0;
    }
  }

  triface fliptets[3], flipedge;
  point pa, pb, pc, pd, pe;
  REAL ori;
  int reducflag = 0;

  fliptets[0] = *flipface;
  fsym(*flipface, fliptets[1]);
  pa = org(fliptets[0]);
  pb = dest(fliptets[0]);
  pc = apex(fliptets[0]);
  pd = oppo(fliptets[0]);
  pe = oppo(fliptets[1]);

  ori = orient3d(pa, pb, pd, pe);
  if (ori > 0) {
    ori = orient3d(pb, pc, pd, pe);
    if (ori > 0) {
      ori = orient3d(pc, pa, pd, pe);
      if (ori > 0) {
        // Found a 2-to-3 flip.
        reducflag = 1;
      } else {
        eprev(*flipface, flipedge); // [c,a]
      }
    } else {
      enext(*flipface, flipedge); // [b,c]
    }
  } else {
    flipedge = *flipface; // [a,b]
  }

  if (reducflag) {
    // A 2-to-3 flip is found.
    flip23(fliptets, 0, fc);
    return 1;
  } else {
    // Try to flip the selected edge of this face.
    if (removeedgebyflips(&flipedge, fc) == 2) {
      return 1;
    }
  }

  // Face is not removed.
  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoveredge()    Recover an edge in current tetrahedralization.           //
//                                                                           //
// If the edge is recovered, 'searchtet' returns a tet containing the edge.  //
//                                                                           //
// This edge may intersect a set of faces and edges in the mesh.  All these  //
// faces or edges are needed to be removed.                                  //
//                                                                           //
// If the parameter 'fullsearch' is set, it tries to flip any face or edge   //
// that intersects the recovering edge.  Otherwise, only the face or edge    //
// which is visible by 'startpt' is tried.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::recoveredgebyflips(point startpt, point endpt, 
                                   triface* searchtet, int fullsearch)
{
  flipconstraints fc;
  enum interresult dir;

  fc.seg[0] = startpt;
  fc.seg[1] = endpt;
  fc.checkflipeligibility = 1;

  // The mainloop of the edge reocvery.
  while (1) { // Loop I

    // Search the edge from 'startpt'.
    point2tetorg(startpt, *searchtet);
    dir = finddirection(searchtet, endpt);
    if (dir == ACROSSVERT) {
      if (dest(*searchtet) == endpt) {
        return 1; // Edge is recovered.
      } else {
        terminatetetgen(3); // // It may be a PLC problem. 
      }
    }

    // The edge is missing. 

    // Try to flip the first intersecting face/edge.
    enextesymself(*searchtet); // Go to the opposite face.
    if (dir == ACROSSFACE) {
      // A face is intersected with the segment. Try to flip it.
      if (removefacebyflips(searchtet, &fc)) {
        continue;
      }
    } else if (dir == ACROSSEDGE) {
      // An edge is intersected with the segment. Try to flip it.
      if (removeedgebyflips(searchtet, &fc) == 2) {
        continue;
      }
    } else {
      terminatetetgen(3); // It may be a PLC problem.
    }

    // The edge is missing.

    if (fullsearch) {
      // Try to flip one of the faces/edges which intersects the edge.
      triface neightet, spintet;
      point pa, pb, pc, pd;
      badface bakface;
      enum interresult dir1;
      int types[2], poss[4], pos = 0;
      int success = 0;
      int t1ver; 
      int i, j;

      // Loop through the sequence of intersecting faces/edges from
      //   'startpt' to 'endpt'.
      point2tetorg(startpt, *searchtet);
      dir = finddirection(searchtet, endpt);
      //assert(dir != ACROSSVERT);

      // Go to the face/edge intersecting the searching edge.
      enextesymself(*searchtet); // Go to the opposite face.
      // This face/edge has been tried in previous step.

      while (1) { // Loop I-I

        // Find the next intersecting face/edge.
        fsymself(*searchtet);
        if (dir == ACROSSFACE) {
          neightet = *searchtet;
          j = (neightet.ver & 3); // j is the current face number.
          for (i = j + 1; i < j + 4; i++) {
            neightet.ver = (i % 4);
            pa = org(neightet);
            pb = dest(neightet);
            pc = apex(neightet);
            pd = oppo(neightet); // The above point.
            if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) {
              dir = (enum interresult) types[0];
              pos = poss[0];
              break;
            } else {
              dir = DISJOINT;
              pos = 0;
            }
          } // i
          // There must be an intersection face/edge.
          assert(dir != DISJOINT);  // SELF_CHECK
        } else {
          assert(dir == ACROSSEDGE);
          while (1) { // Loop I-I-I
            // Check the two opposite faces (of the edge) in 'searchtet'.  
            for (i = 0; i < 2; i++) {
              if (i == 0) {
                enextesym(*searchtet, neightet);
              } else {
                eprevesym(*searchtet, neightet);
              }
              pa = org(neightet);
              pb = dest(neightet);
              pc = apex(neightet);
              pd = oppo(neightet); // The above point.
              if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) {
                dir = (enum interresult) types[0];
                pos = poss[0];
                break; // for loop
              } else {
                dir = DISJOINT;
                pos = 0;
              }
            } // i
            if (dir != DISJOINT) {
              // Find an intersection face/edge.
              break;  // Loop I-I-I
            }
            // No intersection. Rotate to the next tet at the edge.
            fnextself(*searchtet);
          } // while (1) // Loop I-I-I
        }

        // Adjust to the intersecting edge/vertex.
        for (i = 0; i < pos; i++) {
          enextself(neightet);
        }

        if (dir == SHAREVERT) {
          // Check if we have reached the 'endpt'.
          pd = org(neightet);
          if (pd == endpt) {
            // Failed to recover the edge.
            break; // Loop I-I
          } else {
            // We need to further check this case. It might be a PLC problem
            //   or a Steiner point that was added at a bad location.
            assert(0);
          }
        }

        // The next to be flipped face/edge.
        *searchtet = neightet;

        // Bakup this face (tetrahedron).
        bakface.forg = org(*searchtet);
        bakface.fdest = dest(*searchtet);
        bakface.fapex = apex(*searchtet);
        bakface.foppo = oppo(*searchtet);

        // Try to flip this intersecting face/edge.
        if (dir == ACROSSFACE) {
          if (removefacebyflips(searchtet, &fc)) {
            success = 1;
            break; // Loop I-I 
          }
        } else if (dir == ACROSSEDGE) {
          if (removeedgebyflips(searchtet, &fc) == 2) {
            success = 1;
            break; // Loop I-I
          }
        } else {
          assert(0); // A PLC problem.
        }

        // The face/edge is not flipped.
        if ((searchtet->tet == nullptr) ||
            (org(*searchtet) != bakface.forg) ||
            (dest(*searchtet) != bakface.fdest) ||
            (apex(*searchtet) != bakface.fapex) ||
            (oppo(*searchtet) != bakface.foppo)) {
          // 'searchtet' was flipped. We must restore it.
          point2tetorg(bakface.forg, *searchtet);
          dir1 = finddirection(searchtet, bakface.fdest);
          if (dir1 == ACROSSVERT) {
            assert(dest(*searchtet) == bakface.fdest);
            spintet = *searchtet;
            while (1) {
              if (apex(spintet) == bakface.fapex) {
                // Found the face.
                *searchtet = spintet;
                break;
              }
              fnextself(spintet);
              if (spintet.tet == searchtet->tet) {
                searchtet->tet = nullptr;
                break; // Not find.
              }
                } // while (1)
            if (searchtet->tet != nullptr) {
              if (oppo(*searchtet) != bakface.foppo) {
                fsymself(*searchtet);
                if (oppo(*searchtet) != bakface.foppo) {
                  assert(0); // Check this case.
                  searchtet->tet = nullptr;
                  break; // Not find.
                }
              }
            }
          } else {
            searchtet->tet = nullptr; // Not find.
          }
          if (searchtet->tet == nullptr) {
            success = 0; // This face/edge has been destroed.
            break; // Loop I-I 
          }
        }
      } // while (1) // Loop I-I

      if (success) {
        // One of intersecting faces/edges is flipped.
        continue;
      }

    } // if (fullsearch)

    // The edge is missing.
    break; // Loop I

  } // while (1) // Loop I

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// add_steinerpt_in_schoenhardtpoly()    Insert a Steiner point in a Schoen- //
//                                       hardt polyhedron.                   //
//                                                                           //
// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the //
// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)].  Moreover, //
// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'.  A special  //
// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b].       //
// Such set of tets arises when we want to recover an edge from 'p0' to 'p_  //
// (n-1)', and the number of tets at [a,b] can not be reduced by any flip.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n,
                                                 int chkencflag)
{
  triface worktet, *parytet;
  triface faketet1, faketet2;
  point pc, pd, steinerpt;
  insertvertexflags ivf;
  optparameters opm;
  REAL vcd[3], sampt[3], smtpt[3];
  REAL maxminvol = 0.0, minvol = 0.0, ori;
  int success, maxidx = 0;
  int it, i;


  pc = apex(abtets[0]);   // pc = p0
  pd = oppo(abtets[n-1]); // pd = p_(n-1)


  // Find an optimial point in edge [c,d]. It is visible by all outer faces
  //   of 'abtets', and it maxmizes the min volume.

  // initialize the list of 2n boundary faces.
  for (i = 0; i < n; i++) {    
    edestoppo(abtets[i], worktet); // [p_i,p_i+1,a]
    cavetetlist->newindex((void **) &parytet);
    *parytet = worktet;
    eorgoppo(abtets[i], worktet);  // [p_i+1,p_i,b]
    cavetetlist->newindex((void **) &parytet);
    *parytet = worktet;
  }

  int N = 100;
  REAL stepi = 0.01;

  // Search the point along the edge [c,d].
  for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i];

  // Sample N points in edge [c,d].
  for (it = 1; it < N; it++) {
    for (i = 0; i < 3; i++) {
      sampt[i] = pc[i] + (stepi * (double) it) * vcd[i];
    }
    for (i = 0; i < cavetetlist->objects; i++) {
      parytet = (triface *) fastlookup(cavetetlist, i);
      ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt);
      if (i == 0) {
        minvol = ori;
      } else {
        if (minvol > ori) minvol = ori;
      }
    } // i
    if (it == 1) {
      maxminvol = minvol;
      maxidx = it;
    } else {
      if (maxminvol < minvol) {
        maxminvol = minvol;
        maxidx = it;
      } 
    }
  } // it

  if (maxminvol <= 0) {
    cavetetlist->restart();
    return 0;
  }

  for (i = 0; i < 3; i++) {
    smtpt[i] = pc[i] + (stepi * (double) maxidx) * vcd[i];
  }

  // Create two faked tets to hold the two non-existing boundary faces:
  //   [d,c,a] and [c,d,b].
  maketetrahedron(&faketet1);
  setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint);
  cavetetlist->newindex((void **) &parytet);
  *parytet = faketet1;
  maketetrahedron(&faketet2);
  setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint);
  cavetetlist->newindex((void **) &parytet);
  *parytet = faketet2;

  // Point smooth options.
  opm.max_min_volume = 1;
  opm.numofsearchdirs = 20;
  opm.searchstep = 0.001;  
  opm.maxiter = 100; // Limit the maximum iterations.
  opm.initval = 0.0; // Initial volume is zero.

  // Try to relocate the point into the inside of the polyhedron.
  success = smoothpoint(smtpt, cavetetlist, 1, &opm);

  if (success) {
    while (opm.smthiter == 100) {
      // It was relocated and the prescribed maximum iteration reached. 
      // Try to increase the search stepsize.
      opm.searchstep *= 10.0;
      //opm.maxiter = 100; // Limit the maximum iterations.
      opm.initval = opm.imprval;
      opm.smthiter = 0; // Init.
      smoothpoint(smtpt, cavetetlist, 1, &opm);  
    }
  } // if (success)

  // Delete the two faked tets.
  tetrahedrondealloc(faketet1.tet);
  tetrahedrondealloc(faketet2.tet);

  cavetetlist->restart();

  if (!success) {
    return 0;
  }


  // Insert the Steiner point.
  makepoint(&steinerpt, FREEVOLVERTEX);
  for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i];

  // Insert the created Steiner point.
  for (i = 0; i < n; i++) {
    infect(abtets[i]);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = abtets[i];
  }
  worktet = abtets[0]; // No need point location.
  ivf.iloc = (int) INSTAR;
  ivf.chkencflag = chkencflag;
  ivf.assignmeshsize = b->metric; 
  if (ivf.assignmeshsize) {
    // Search the tet containing 'steinerpt' for size interpolation.
    locate(steinerpt, &(abtets[0]), 0);
    worktet = abtets[0];
  }

  // Insert the new point into the tetrahedralization T.
  // Note that T is convex (nonconvex = 0).
  if (insertpoint(steinerpt, &worktet, nullptr, nullptr, &ivf)) {
    // The vertex has been inserted.
    st_volref_count++; 
    if (steinerleft > 0) steinerleft--;
    return 1;
  } else {
    // Not inserted. 
    pointdealloc(steinerpt);
    return 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// add_steinerpt_in_segment()    Add a Steiner point inside a segment.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel)
{
  triface searchtet;
  face *paryseg, candseg;
  point startpt, endpt, pc, pd;
  flipconstraints fc;
  enum interresult dir;
  REAL P[3], Q[3], tp, tq;
  REAL len, smlen = 0, split = 0, split_q = 0;
  int success;
  int i;

  startpt = sorg(*misseg);
  endpt = sdest(*misseg);

  fc.seg[0] = startpt;
  fc.seg[1] = endpt;
  fc.checkflipeligibility = 1;
  fc.collectencsegflag = 1;

  point2tetorg(startpt, searchtet);
  dir = finddirection(&searchtet, endpt);
  //assert(dir != ACROSSVERT);

  // Try to flip the first intersecting face/edge.
  enextesymself(searchtet); // Go to the opposite face.

  int bak_fliplinklevel = b->fliplinklevel;
  b->fliplinklevel = searchlevel;

  if (dir == ACROSSFACE) {
    // A face is intersected with the segment. Try to flip it.
    success = removefacebyflips(&searchtet, &fc);
    assert(success == 0);
  } else if (dir == ACROSSEDGE) {
    // An edge is intersected with the segment. Try to flip it.
    success = removeedgebyflips(&searchtet, &fc);
    assert(success != 2);
  } else {
    terminatetetgen(3); // It may be a PLC problem.
  }

  split = 0;
  for (i = 0; i < caveencseglist->objects; i++) {
    paryseg = (face *) fastlookup(caveencseglist, i);
    suninfect(*paryseg);
    // Calculate the shortest edge betwenn the two lines.
    pc = sorg(*paryseg);
    pd = sdest(*paryseg);
    tp = tq = 0;
    if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) {
      // Does the shortest edge lie between the two segments? 
      // Round tp and tq.
      if ((tp > 0) && (tq < 1)) {
        if (tp < 0.5) {
          if (tp < (b->epsilon * 1e+3)) tp = 0.0;
        } else {
          if ((1.0 - tp) < (b->epsilon * 1e+3)) tp = 1.0;
        }
      }
      if ((tp <= 0) || (tp >= 1)) continue; 
      if ((tq > 0) && (tq < 1)) {
        if (tq < 0.5) {
          if (tq < (b->epsilon * 1e+3)) tq = 0.0;
        } else {
          if ((1.0 - tq) < (b->epsilon * 1e+3)) tq = 1.0;
        }
      }
      if ((tq <= 0) || (tq >= 1)) continue;
      // It is a valid shortest edge. Calculate its length.
      len = distance(P, Q);
      if (split == 0) {
        smlen = len;
        split = tp;
        split_q = tq;
        candseg = *paryseg;
      } else {
        if (len < smlen) {
          smlen = len;
          split = tp;
          split_q = tq;
          candseg = *paryseg;
        }
      }
    }
  }

  caveencseglist->restart();
  b->fliplinklevel = bak_fliplinklevel;

  if (split == 0) {
    // Found no crossing segment. 
    return 0;
  }

  face splitsh;
  face splitseg;
  point steinerpt, *parypt;
  insertvertexflags ivf;

  if (b->addsteiner_algo == 1) {
    // Split the segment at the closest point to a near segment.
    makepoint(&steinerpt, FREESEGVERTEX);
    for (i = 0; i < 3; i++) {
      steinerpt[i] = startpt[i] + split * (endpt[i] - startpt[i]);
    }
  } else { // b->addsteiner_algo == 2
    for (i = 0; i < 3; i++) {
      P[i] = startpt[i] + split * (endpt[i] - startpt[i]);
    }
    pc = sorg(candseg);
    pd = sdest(candseg);
    for (i = 0; i < 3; i++) {
      Q[i] = pc[i] + split_q * (pd[i] - pc[i]);
    }
    makepoint(&steinerpt, FREEVOLVERTEX);
    for (i = 0; i < 3; i++) {
      steinerpt[i] = 0.5 * (P[i] + Q[i]);
    }
  }

  // We need to locate the point. Start searching from 'searchtet'.
  if (split < 0.5) {
    point2tetorg(startpt, searchtet);
  } else {
    point2tetorg(endpt, searchtet);
  }
  if (b->addsteiner_algo == 1) {
    splitseg = *misseg;
    spivot(*misseg, splitsh);
  } else {
    splitsh.sh = nullptr;
    splitseg.sh = nullptr;
  }
  ivf.iloc = (int) OUTSIDE;
  ivf.bowywat = 1;
  ivf.lawson = 0;
  ivf.rejflag = 0;
  ivf.chkencflag = 0;
  ivf.sloc = (int) ONEDGE;
  ivf.sbowywat = 1;
  ivf.splitbdflag = 0;
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric; 

  if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) {
    pointdealloc(steinerpt);
    return 0;
  }

  if (b->addsteiner_algo == 1) {
    // Save this Steiner point (for removal).
    //   Re-use the array 'subvertstack'.
    subvertstack->newindex((void **) &parypt);
    *parypt = steinerpt;
    st_segref_count++;
  } else { // b->addsteiner_algo == 2
    // Queue the segment for recovery.
    subsegstack->newindex((void **) &paryseg);
    *paryseg = *misseg; 
    st_volref_count++;
  }
  if (steinerleft > 0) steinerleft--;

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// addsteiner4recoversegment()    Add a Steiner point for recoveing a seg.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag)
{
  triface *abtets, searchtet, spintet;
  face splitsh;
  face *paryseg;
  point startpt, endpt;
  point pa, pb, pd, steinerpt, *parypt;
  enum interresult dir;
  insertvertexflags ivf;
  int types[2], poss[4];
  int n, endi, success;
  int t1ver;
  int i;

  startpt = sorg(*misseg);
  if (pointtype(startpt) == FREESEGVERTEX) {
    sesymself(*misseg);
    startpt = sorg(*misseg);
  }
  endpt = sdest(*misseg);

  // Try to recover the edge by adding Steiner points.
  point2tetorg(startpt, searchtet);
  dir = finddirection(&searchtet, endpt);
  enextself(searchtet); 
  //assert(apex(searchtet) == startpt);

  if (dir == ACROSSFACE) {
    // The segment is crossing at least 3 faces. Find the common edge of 
    //   the first 3 crossing faces.
    esymself(searchtet);
    fsym(searchtet, spintet);
    pd = oppo(spintet);
    for (i = 0; i < 3; i++) {
      pa = org(spintet);
      pb = dest(spintet);
      //pc = apex(neightet);
      if (tri_edge_test(pa, pb, pd, startpt, endpt, nullptr, 1, types, poss)) {
        break; // Found the edge.
      }
      enextself(spintet);
      eprevself(searchtet);
    }
    assert(i < 3);
    esymself(searchtet);        
  } else {
    assert(dir == ACROSSEDGE);
    // PLC check.
    if (issubseg(searchtet)) {
      face checkseg;
      tsspivot1(searchtet, checkseg);
      printf("Found two segments intersect each other.\n");
      pa = farsorg(*misseg);
      pb = farsdest(*misseg);
      printf("  1st: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), 
             shellmark(*misseg));
      pa = farsorg(checkseg);
      pb = farsdest(checkseg);
      printf("  2nd: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), 
             shellmark(checkseg));
      terminatetetgen(3);
    }
  }
  assert(apex(searchtet) == startpt);

  spintet = searchtet;
  n = 0; endi = -1;
  while (1) {
    // Check if the endpt appears in the star.
    if (apex(spintet) == endpt) {
      endi = n; // Remember the position of endpt.
    }
    n++; // Count a tet in the star.
    fnextself(spintet);
    if (spintet.tet == searchtet.tet) break;
  }
  assert(n >= 3);

  if (endi > 0) {
    // endpt is also in the edge star
    // Get all tets in the edge star.
    abtets = new triface[n];
    spintet = searchtet;
    for (i = 0; i < n; i++) {
      abtets[i] = spintet;
      fnextself(spintet);
    }

    success = 0;

    if (dir == ACROSSFACE) {
      // Find a Steiner points inside the polyhedron.
      if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) {
        success = 1;
      }
    } else if (dir == ACROSSEDGE) {
      if (n > 4) {
        // In this case, 'abtets' is separated by the plane (containing the
        //   two intersecting edges) into two parts, P1 and P2, where P1
        //   consists of 'endi' tets: abtets[0], abtets[1], ..., 
        //   abtets[endi-1], and P2 consists of 'n - endi' tets: 
        //   abtets[endi], abtets[endi+1], abtets[n-1].
        if (endi > 2) { // P1
          // There are at least 3 tets in the first part.
          if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) {
            success++;
          }
        }
        if ((n - endi) > 2) { // P2
          // There are at least 3 tets in the first part.
          if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, 0)) {
            success++;
          }
        }
      } else {
        // In this case, a 4-to-4 flip should be re-cover the edge [c,d].
        //   However, there will be invalid tets (either zero or negtive 
        //   volume). Otherwise, [c,d] should already be recovered by the 
        //   recoveredge() function.
        assert(0); // DEBUG IT
      }
    } else {
      assert(0); // A PLC problem.
    }

    delete [] abtets;

    if (success) {
      // Add the missing segment back to the recovering list.
      subsegstack->newindex((void **) &paryseg);
      *paryseg = *misseg;
      return 1;
    }
  } // if (endi > 0)

  if (!splitsegflag) {
    return 0;
  }

  if (b->verbose > 2) {
    printf("      Splitting segment (%d, %d)\n", pointmark(startpt), 
           pointmark(endpt));
  }
  steinerpt = nullptr;

  if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2
    if (add_steinerpt_in_segment(misseg, 3)) {
      return 1;
    }
    sesymself(*misseg);
    if (add_steinerpt_in_segment(misseg, 3)) {
      return 1;
    }
    sesymself(*misseg);
  }




  if (steinerpt == nullptr) {
    // Split the segment at its midpoint.
    makepoint(&steinerpt, FREESEGVERTEX);
    for (i = 0; i < 3; i++) {
      steinerpt[i] = 0.5 * (startpt[i] + endpt[i]);
    }

    // We need to locate the point.
    assert(searchtet.tet != nullptr); // Start searching from 'searchtet'.
    spivot(*misseg, splitsh);
    ivf.iloc = (int) OUTSIDE;
    ivf.bowywat = 1;
    ivf.lawson = 0;
    ivf.rejflag = 0;
    ivf.chkencflag = 0;
    ivf.sloc = (int) ONEDGE;
    ivf.sbowywat = 1;
    ivf.splitbdflag = 0;
    ivf.validflag = 1;
    ivf.respectbdflag = 1;
    ivf.assignmeshsize = b->metric; 
    if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) {
      assert(0);
    }
  } // if (endi > 0)

  // Save this Steiner point (for removal).
  //   Re-use the array 'subvertstack'.
  subvertstack->newindex((void **) &parypt);
  *parypt = steinerpt;

  st_segref_count++;
  if (steinerleft > 0) steinerleft--;

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoversegments()    Recover all segments.                                //
//                                                                           //
// All segments need to be recovered are in 'subsegstack'.                   //
//                                                                           //
// This routine first tries to recover each segment by only using flips. If  //
// no flip is possible, and the flag 'steinerflag' is set, it then tries to  //
// insert Steiner points near or in the segment.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch,
                                int steinerflag)
{
  triface searchtet, spintet;
  face sseg, *paryseg;
  point startpt, endpt;
  int success;
  int t1ver;
  long bak_inpoly_count = st_volref_count; 
  long bak_segref_count = st_segref_count;

  if (b->verbose > 1) {
    printf("    Recover segments [%s level = %2d] #:  %ld.\n",
           (b->fliplinklevel > 0) ? "fixed" : "auto",
           (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
           subsegstack->objects);
  }

  // Loop until 'subsegstack' is empty.
  while (subsegstack->objects > 0l) {
    // seglist is used as a stack.
    subsegstack->objects--;
    paryseg = (face *) fastlookup(subsegstack, subsegstack->objects);
    sseg = *paryseg;

    // Check if this segment has been recovered.
    sstpivot1(sseg, searchtet);
    if (searchtet.tet != nullptr) {
      continue; // Not a missing segment.
    }

    startpt = sorg(sseg);
    endpt = sdest(sseg);

    if (b->verbose > 2) {
      printf("      Recover segment (%d, %d).\n", pointmark(startpt), 
             pointmark(endpt));
    }

    success = 0;

    if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) {
      success = 1;
    } else {
      // Try to recover it from the other direction.
      if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
        success = 1;
      }
    }

    if (!success && fullsearch) {
      if (recoveredgebyflips(startpt, endpt, &searchtet, fullsearch)) {
        success = 1;
      }
    }

    if (success) {
      // Segment is recovered. Insert it.
      // Let the segment remember an adjacent tet.
      sstbond1(sseg, searchtet);
      // Bond the segment to all tets containing it.
      spintet = searchtet;
      do {
        tssbond1(spintet, sseg);
        fnextself(spintet);
      } while (spintet.tet != searchtet.tet);
    } else {
      if (steinerflag > 0) {
        // Try to recover the segment but do not split it.
        if (addsteiner4recoversegment(&sseg, 0)) {
          success = 1;
        }
        if (!success && (steinerflag > 1)) {
          // Split the segment.
          addsteiner4recoversegment(&sseg, 1);
          success = 1;
        }
      }
      if (!success) {
        if (misseglist != nullptr) {
          // Save this segment.
          misseglist->newindex((void **) &paryseg);
          *paryseg = sseg;
        }
      }
    }

  } // while (subsegstack->objects > 0l)

  if (steinerflag) {
    if (b->verbose > 1) {
      // Report the number of added Steiner points.
      if (st_volref_count > bak_inpoly_count) {
        printf("    Add %ld Steiner points in volume.\n", 
               st_volref_count - bak_inpoly_count);
      }
      if (st_segref_count > bak_segref_count) {
        printf("    Add %ld Steiner points in segments.\n", 
               st_segref_count - bak_segref_count);
      }
    }
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoverfacebyflips()    Recover a face by flips.                          //
//                                                                           //
// If 'searchsh' is not nullptr, it is a subface to be recovered.  It is only   //
// used for checking self-intersections.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, 
                                   face *searchsh, triface* searchtet)
{
  triface spintet, flipedge;
  point pd, pe;
  enum interresult dir;
  flipconstraints fc;
  int types[2], poss[4], intflag;
  int success, success1;
  int t1ver; 
  int i, j;


  fc.fac[0] = pa;
  fc.fac[1] = pb;
  fc.fac[2] = pc;
  fc.checkflipeligibility = 1;
  success = 0;

  for (i = 0; i < 3 && !success; i++) {
    while (1) {
      // Get a tet containing the edge [a,b].
      point2tetorg(fc.fac[i], *searchtet);
      dir = finddirection(searchtet, fc.fac[(i+1)%3]);
      //assert(dir == ACROSSVERT);
      assert(dest(*searchtet) == fc.fac[(i+1)%3]);
      // Search the face [a,b,c]
      spintet = *searchtet;
      while (1) {
        if (apex(spintet) == fc.fac[(i+2)%3]) {
          // Found the face.
          *searchtet = spintet;
          // Return the face [a,b,c].
          for (j = i; j > 0; j--) {
            eprevself(*searchtet);
          }
          success = 1;
          break;
        }
        fnextself(spintet);
        if (spintet.tet == searchtet->tet) break;
      } // while (1)
      if (success) break;
      // The face is missing. Try to recover it.
      success1 = 0;
      // Find a crossing edge of this face.
      spintet = *searchtet;
      while (1) {
        pd = apex(spintet);
        pe = oppo(spintet);
        if ((pd != dummypoint) && (pe != dummypoint)) {
          // Check if [d,e] intersects [a,b,c]
          intflag = tri_edge_test(pa, pb, pc, pd, pe, nullptr, 1, types, poss);
          if (intflag > 0) {
            // By our assumptions, they can only intersect at a single point.
            if (intflag == 2) {
              // Check the intersection type.
              dir = (enum interresult) types[0];
              if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
                // Go to the edge [d,e].
                edestoppo(spintet, flipedge); // [d,e,a,b]
                if (searchsh != nullptr) {
                  // Check if [e,d] is a segment.
                  if (issubseg(flipedge)) {
                    if (!b->quiet) {
                        face checkseg;
                        tsspivot1(flipedge, checkseg);
                        printf("Found a segment and a subface intersect.\n");
                        pd = farsorg(checkseg);
                        pe = farsdest(checkseg);
                        printf("  1st: [%d, %d] %d.\n",  pointmark(pd), 
                               pointmark(pe), shellmark(checkseg)); 
                        printf("  2nd: [%d,%d,%d] %d\n", pointmark(pa), 
                               pointmark(pb), pointmark(pc), shellmark(*searchsh));
                    }
                    // [Bruno] Keep track of intersecting facets.
                    in->isectfaces.insert(shellmark(*searchsh));
                    terminatetetgen(3);
                  }
                }
                // Try to flip the edge [d,e].
                success1 = (removeedgebyflips(&flipedge, &fc) == 2);
              } else {
                if (dir == TOUCHFACE) {
                  point touchpt, *parypt;
                  if (poss[1] == 0) {
                    touchpt = pd; // pd is a coplanar vertex.
                  } else {
                    touchpt = pe; // pe is a coplanar vertex.
                  }
                  if (pointtype(touchpt) == FREEVOLVERTEX) {
                    // A volume Steiner point was added in this subface.
                    // Split this subface by this point.
                    face checksh, *parysh;
                    int siloc = (int) ONFACE;
                    int sbowat = 0; // Only split this subface.
                    setpointtype(touchpt, FREEFACETVERTEX);
                    sinsertvertex(touchpt, searchsh, nullptr, siloc, sbowat, 0);
                    st_volref_count--;
                    st_facref_count++;
                    // Queue this vertex for removal.
                    subvertstack->newindex((void **) &parypt);
                    *parypt = touchpt;
                    // Queue new subfaces for recovery.
                    // Put all new subfaces into stack for recovery.
                    for (i = 0; i < caveshbdlist->objects; i++) {
                      // Get an old subface at edge [a, b].
                      parysh = (face *) fastlookup(caveshbdlist, i);
                      spivot(*parysh, checksh); // The new subface [a, b, p].
                      // Do not recover a deleted new face (degenerated).
                      if (checksh.sh[3] != nullptr) {
                        subfacstack->newindex((void **) &parysh);
                        *parysh = checksh;
                      }
                    }
                    // Delete the old subfaces in sC(p).
                    assert(caveshlist->objects == 1);
                    for (i = 0; i < caveshlist->objects; i++) {
                      parysh = (face *) fastlookup(caveshlist, i);
                      shellfacedealloc(subfaces, parysh->sh);
                    }
                    // Clear working lists.
                    caveshlist->restart();
                    caveshbdlist->restart();
                    cavesegshlist->restart();
                    // We can return this function.
                    searchsh->sh = nullptr; // It has been split.
                    success1 = 0;
                    success = 1; 
                  } else {
                    // It should be a PLC problem.
                    if (pointtype(touchpt) == FREESEGVERTEX) {
                      // A segment and a subface intersect. 
                    } else if (pointtype(touchpt) == FREEFACETVERTEX) {
                      // Two facets self-intersect.
                    }
                    terminatetetgen(3);
                  }
                } else {
                  assert(0); // Unknown cases. Debug.
                }
              }
              break;
            } else { // intflag == 4. Coplanar case.
              // This may be an input PLC error.
              assert(0);
            }
          } // if (intflag > 0)
        }
        fnextself(spintet);
        assert(spintet.tet != searchtet->tet);
      } // while (1)
      if (!success1) break;
    } // while (1)
  } // i

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoversubfaces()    Recover all subfaces.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag)
{
  triface searchtet, neightet, spintet;
  face searchsh, neighsh, neineish, *parysh;
  face bdsegs[3];
  point startpt, endpt, apexpt, *parypt;
  point steinerpt;
  enum interresult dir;
  insertvertexflags ivf;
  int success;
  int t1ver;
  int i, j;

  if (b->verbose > 1) {
    printf("    Recover subfaces [%s level = %2d] #:  %ld.\n",
           (b->fliplinklevel > 0) ? "fixed" : "auto",
           (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
           subfacstack->objects);
  }

  // Loop until 'subfacstack' is empty.
  while (subfacstack->objects > 0l) {

    subfacstack->objects--;
    parysh = (face *) fastlookup(subfacstack, subfacstack->objects);
    searchsh = *parysh;

    if (searchsh.sh[3] == nullptr) continue; // Skip a dead subface.

    stpivot(searchsh, neightet);
    if (neightet.tet != nullptr) continue; // Skip a recovered subface.


    if (b->verbose > 2) {
      printf("      Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)),
             pointmark(sdest(searchsh)), pointmark(sapex(searchsh)));
    }

    // The three edges of the face need to be existed first.
    for (i = 0; i < 3; i++) {
      sspivot(searchsh, bdsegs[i]);   
      if (bdsegs[i].sh != nullptr) {
        // The segment must exist.
        sstpivot1(bdsegs[i], searchtet);
        if (searchtet.tet == nullptr) {
          assert(0);
        }
      } else {
        // This edge is not a segment (due to a Steiner point).
        // Check whether it exists or not.
        success = 0;
        startpt = sorg(searchsh);
        endpt = sdest(searchsh);
        point2tetorg(startpt, searchtet);
        dir = finddirection(&searchtet, endpt);
        if (dir == ACROSSVERT) {
          if (dest(searchtet) == endpt) {
            success = 1;  
          } else {
            //assert(0); // A PLC problem.
            terminatetetgen(3);
          }
        } else {
          // The edge is missing. Try to recover it.
          if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) {
            success = 1;
          } else {
            if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
              success = 1;
            }
          }
        }
        if (success) {
          // Insert a temporary segment to protect this edge.
          makeshellface(subsegs, &(bdsegs[i]));
          setshvertices(bdsegs[i], startpt, endpt, nullptr);
          smarktest2(bdsegs[i]); // It's a temporary segment.
          // Insert this segment into surface mesh.
          ssbond(searchsh, bdsegs[i]);
          spivot(searchsh, neighsh);
          if (neighsh.sh != nullptr) {
            ssbond(neighsh, bdsegs[i]);
          }
          // Insert this segment into tetrahedralization.
          sstbond1(bdsegs[i], searchtet);
          // Bond the segment to all tets containing it.
          spintet = searchtet;
          do {
            tssbond1(spintet, bdsegs[i]);
            fnextself(spintet);
          } while (spintet.tet != searchtet.tet);
        } else {
          // An edge of this subface is missing. Can't recover this subface.
          // Delete any temporary segment that has been created.
          for (j = (i - 1); j >= 0; j--) {
            if (smarktest2ed(bdsegs[j])) { 
              spivot(bdsegs[j], neineish);
              assert(neineish.sh != nullptr);
              //if (neineish.sh != nullptr) {
                ssdissolve(neineish);
                spivot(neineish, neighsh);
                if (neighsh.sh != nullptr) {
                  ssdissolve(neighsh);
                  // There should be only two subfaces at this segment.
                  spivotself(neighsh); // SELF_CHECK
                  assert(neighsh.sh == neineish.sh);
                }
                  //}
              sstpivot1(bdsegs[j], searchtet);
              assert(searchtet.tet != nullptr);
              //if (searchtet.tet != nullptr) {
                spintet = searchtet;
                while (1) {
                  tssdissolve1(spintet);
                  fnextself(spintet);
                  if (spintet.tet == searchtet.tet) break;
                }
                  //}
              shellfacedealloc(subsegs, bdsegs[j].sh);
            }
          } // j
          if (steinerflag) {
            // Add a Steiner point at the midpoint of this edge.
            if (b->verbose > 2) {
              printf("      Add a Steiner point in subedge (%d, %d).\n",
                     pointmark(startpt), pointmark(endpt));
            }
            makepoint(&steinerpt, FREEFACETVERTEX);
            for (j = 0; j < 3; j++) {
              steinerpt[j] = 0.5 * (startpt[j] + endpt[j]);
            }

            point2tetorg(startpt, searchtet); // Start from 'searchtet'.
            ivf.iloc = (int) OUTSIDE; // Need point location.
            ivf.bowywat = 1;
            ivf.lawson = 0;
            ivf.rejflag = 0;
            ivf.chkencflag = 0;
            ivf.sloc = (int) ONEDGE;            
            ivf.sbowywat = 1; // Allow flips in facet.
            ivf.splitbdflag = 0;
            ivf.validflag = 1;
            ivf.respectbdflag = 1;
            ivf.assignmeshsize = b->metric;
            if (!insertpoint(steinerpt, &searchtet, &searchsh, nullptr, &ivf)) {
              assert(0);
            }
            // Save this Steiner point (for removal).
            //   Re-use the array 'subvertstack'.
            subvertstack->newindex((void **) &parypt);
            *parypt = steinerpt;

            st_facref_count++;
            if (steinerleft > 0) steinerleft--;
          } // if (steinerflag)
          break;
        }
      }
      senextself(searchsh);
    } // i

    if (i == 3) {
      // Recover the subface.
      startpt = sorg(searchsh);
      endpt   = sdest(searchsh);
      apexpt  = sapex(searchsh);

      success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet);

      // Delete any temporary segment that has been created.
      for (j = 0; j < 3; j++) {
        if (smarktest2ed(bdsegs[j])) { 
          spivot(bdsegs[j], neineish);
          assert(neineish.sh != nullptr);
          //if (neineish.sh != nullptr) {
            ssdissolve(neineish);
            spivot(neineish, neighsh);
            if (neighsh.sh != nullptr) {
              ssdissolve(neighsh);
              // There should be only two subfaces at this segment.
              spivotself(neighsh); // SELF_CHECK
              assert(neighsh.sh == neineish.sh);
            }
              //}
          sstpivot1(bdsegs[j], neightet);
          assert(neightet.tet != nullptr);
          //if (neightet.tet != nullptr) {
            spintet = neightet;
            while (1) {
              tssdissolve1(spintet);
              fnextself(spintet);
              if (spintet.tet == neightet.tet) break;
            }
              //}
          shellfacedealloc(subsegs, bdsegs[j].sh);
        }
      } // j

      if (success) {
        if (searchsh.sh != nullptr) {
          // Face is recovered. Insert it.
          tsbond(searchtet, searchsh);
          fsymself(searchtet);
          sesymself(searchsh);
          tsbond(searchtet, searchsh);
        }
      } else {
        if (steinerflag) {
          // Add a Steiner point at the barycenter of this subface.
          if (b->verbose > 2) {
            printf("      Add a Steiner point in subface (%d, %d, %d).\n",
                   pointmark(startpt), pointmark(endpt), pointmark(apexpt));
          }
          makepoint(&steinerpt, FREEFACETVERTEX);
          for (j = 0; j < 3; j++) {
            steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
          }

          point2tetorg(startpt, searchtet); // Start from 'searchtet'.
          ivf.iloc = (int) OUTSIDE; // Need point location.
          ivf.bowywat = 1;
          ivf.lawson = 0;
          ivf.rejflag = 0;
          ivf.chkencflag = 0;
          ivf.sloc = (int) ONFACE;          
          ivf.sbowywat = 1; // Allow flips in facet.
          ivf.splitbdflag = 0;
          ivf.validflag = 1;
          ivf.respectbdflag = 1;
          ivf.assignmeshsize = b->metric; 
          if (!insertpoint(steinerpt, &searchtet, &searchsh, nullptr, &ivf)) {
            assert(0);
          }
          // Save this Steiner point (for removal).
          //   Re-use the array 'subvertstack'.
          subvertstack->newindex((void **) &parypt);
          *parypt = steinerpt;

          st_facref_count++;
          if (steinerleft > 0) steinerleft--;
        } // if (steinerflag)
      }
    } else {
      success = 0;      
    }

    if (!success) {
      if (misshlist != nullptr) {
        // Save this subface.
        misshlist->newindex((void **) &parysh);
        *parysh = searchsh;
      }
    }

  } // while (subfacstack->objects > 0l)

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getvertexstar()    Return the star of a vertex.                           //
//                                                                           //
// If the flag 'fullstar' is set, return the complete star of this vertex.   //
// Otherwise, only a part of the star which is bounded by facets is returned.// 
//                                                                           //
// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'.  //
// Every tet in 'tetlist' is at the face oppsiting to 'searchpt'.            //
//                                                                           //
// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). //
//                                                                           //
// 'shlist' returns the list of subfaces in the star. Each subface must face //
// to the interior of this star.                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, 
                              arraypool* vertlist, arraypool* shlist)
{
  triface searchtet, neightet, *parytet;
  face checksh, *parysh;
  point pt, *parypt;
  int collectflag;
  int t1ver;
  int i, j;

  point2tetorg(searchpt, searchtet);

  // Go to the opposite face (the link face) of the vertex.
  enextesymself(searchtet);
  //assert(oppo(searchtet) == searchpt);
  infect(searchtet); // Collect this tet (link face).
  tetlist->newindex((void **) &parytet);
  *parytet = searchtet;
  if (vertlist != nullptr) {
    // Collect three (link) vertices.
    j = (searchtet.ver & 3); // The current vertex index.
    for (i = 1; i < 4; i++) {
      pt = (point) searchtet.tet[4 + ((j + i) % 4)];
      pinfect(pt);
      vertlist->newindex((void **) &parypt);
      *parypt = pt;
    }
  }

  collectflag = 1;
  esym(searchtet, neightet);
  if (issubface(neightet)) {
    if (shlist != nullptr) {
      tspivot(neightet, checksh);
      if (!sinfected(checksh)) {
        // Collect this subface (link edge).
        sinfected(checksh);
        shlist->newindex((void **) &parysh);
        *parysh = checksh;
      }
    } 
    if (!fullstar) {
      collectflag = 0;
    }
  }
  if (collectflag) {
    fsymself(neightet); // Goto the adj tet of this face.
    esymself(neightet); // Goto the oppo face of this vertex.
    // assert(oppo(neightet) == searchpt);
    infect(neightet); // Collect this tet (link face).
    tetlist->newindex((void **) &parytet);
    *parytet = neightet;
    if (vertlist != nullptr) {
      // Collect its apex.
      pt = apex(neightet);
      pinfect(pt);
      vertlist->newindex((void **) &parypt);
      *parypt = pt;
    }
  } // if (collectflag)

  // Continue to collect all tets in the star.
  for (i = 0; i < tetlist->objects; i++) {
    searchtet = * (triface *) fastlookup(tetlist, i);
    // Note that 'searchtet' is a face opposite to 'searchpt', and the neighbor
    //   tet at the current edge is already collected.
    // Check the neighors at the other two edges of this face.
    for (j = 0; j < 2; j++) {
      collectflag = 1;
      enextself(searchtet);
      esym(searchtet, neightet);
      if (issubface(neightet)) {
        if (shlist != nullptr) {
          tspivot(neightet, checksh);
          if (!sinfected(checksh)) {
            // Collect this subface (link edge).
            sinfected(checksh);
            shlist->newindex((void **) &parysh);
            *parysh = checksh;
          }
        }
        if (!fullstar) {
          collectflag = 0;
        }
      }
      if (collectflag) {
        fsymself(neightet);
        if (!infected(neightet)) {
          esymself(neightet); // Go to the face opposite to 'searchpt'.
          infect(neightet);
          tetlist->newindex((void **) &parytet);
          *parytet = neightet;
          if (vertlist != nullptr) {
            // Check if a vertex is collected.
            pt = apex(neightet);
            if (!pinfected(pt)) {
              pinfect(pt);
              vertlist->newindex((void **) &parypt);
              *parypt = pt;
            }
          }
        } // if (!infected(neightet))
      } // if (collectflag)
    } // j
  } // i


  // Uninfect the list of tets and vertices.
  for (i = 0; i < tetlist->objects; i++) {
    parytet = (triface *) fastlookup(tetlist, i);
    uninfect(*parytet);
  }

  if (vertlist != nullptr) {
    for (i = 0; i < vertlist->objects; i++) {
      parypt = (point *) fastlookup(vertlist, i);
      puninfect(*parypt);
    }
  }

  if (shlist != nullptr) {
    for (i = 0; i < shlist->objects; i++) {
      parysh = (face *) fastlookup(shlist, i);
      suninfect(*parysh);
    }
  }

  return (int) tetlist->objects;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getedge()    Get a tetrahedron having the two endpoints.                  //
//                                                                           //
// The method here is to search the second vertex in the link faces of the   //
// first vertex. The global array 'cavetetlist' is re-used for searching.    //
//                                                                           //
// This function is used for the case when the mesh is non-convex. Otherwise,//
// the function finddirection() should be faster than this.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::getedge(point e1, point e2, triface *tedge)
{
  triface searchtet, neightet, *parytet;
  point pt;
  int done;
  int i, j;

  if (b->verbose > 2) {
    printf("      Get edge from %d to %d.\n", pointmark(e1), pointmark(e2));
  }

  // Quickly check if 'tedge' is just this edge.
  if (!isdeadtet(*tedge)) {
    if (org(*tedge) == e1) {
      if (dest(*tedge) == e2) {
        return 1;
      }
    } else if (org(*tedge) == e2) {
      if (dest(*tedge) == e1) {
        esymself(*tedge);
        return 1;
      }
    }
  }

  // Search for the edge [e1, e2].
  point2tetorg(e1, *tedge);
  finddirection(tedge, e2);
  if (dest(*tedge) == e2) {
    return 1;
  } else {
    // Search for the edge [e2, e1].
    point2tetorg(e2, *tedge);
    finddirection(tedge, e1);
    if (dest(*tedge) == e1) {
      esymself(*tedge);
      return 1;
    }
  }


  // Go to the link face of e1.
  point2tetorg(e1, searchtet);
  enextesymself(searchtet);
  //assert(oppo(searchtet) == e1);

  assert(cavetetlist->objects == 0l); // It will re-use this list.

  // Search e2.
  for (i = 0; i < 3; i++) {
    pt = apex(searchtet);
    if (pt == e2) {
      // Found. 'searchtet' is [#,#,e2,e1].
      eorgoppo(searchtet, *tedge); // [e1,e2,#,#].
      return 1;
    }
    enextself(searchtet);
  }

  // Get the adjacent link face at 'searchtet'.
  fnext(searchtet, neightet);
  esymself(neightet);
  // assert(oppo(neightet) == e1);
  pt = apex(neightet);
  if (pt == e2) {
    // Found. 'neightet' is [#,#,e2,e1].
    eorgoppo(neightet, *tedge); // [e1,e2,#,#].
    return 1;
  }

  // Continue searching in the link face of e1.
  infect(searchtet);
  cavetetlist->newindex((void **) &parytet);
  *parytet = searchtet;
  infect(neightet);
  cavetetlist->newindex((void **) &parytet);
  *parytet = neightet;

  done = 0;

  for (i = 0; (i < cavetetlist->objects) && !done; i++) {
    parytet = (triface *) fastlookup(cavetetlist, i);
    searchtet = *parytet;
    for (j = 0; (j < 2) && !done; j++) {
      enextself(searchtet);
      fnext(searchtet, neightet);
      if (!infected(neightet)) {        
        esymself(neightet);
        pt = apex(neightet);
        if (pt == e2) {
          // Found. 'neightet' is [#,#,e2,e1].
          eorgoppo(neightet, *tedge);
          done = 1;
        } else {
          infect(neightet);
          cavetetlist->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    } // j
  } // i 

  // Uninfect the list of visited tets.
  for (i = 0; i < cavetetlist->objects; i++) {
    parytet = (triface *) fastlookup(cavetetlist, i);
    uninfect(*parytet);
  }
  cavetetlist->restart();

  return done;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// reduceedgesatvertex()    Reduce the number of edges at a given vertex.    //
//                                                                           //
// 'endptlist' contains the endpoints of edges connecting at the vertex.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist)
{
  triface searchtet;
  point *pendpt, *parypt;
  enum interresult dir;
  flipconstraints fc;
  int reduceflag;
  int count;
  int n, i, j;


  fc.remvert = startpt;
  fc.checkflipeligibility = 1;

  while (1) {

    count = 0;

    for (i = 0; i < endptlist->objects; i++) {
      pendpt = (point *) fastlookup(endptlist, i);
      if (*pendpt == dummypoint) {
        continue; // Do not reduce a virtual edge.
      }
      reduceflag = 0;
      // Find the edge.
      if (nonconvex) {
        if (getedge(startpt, *pendpt, &searchtet)) {
          dir = ACROSSVERT;
        } else {
          // The edge does not exist (was flipped).
          dir = INTERSECT;
        }
      } else {
        point2tetorg(startpt, searchtet);
        dir = finddirection(&searchtet, *pendpt);
      }
      if (dir == ACROSSVERT) {
        if (dest(searchtet) == *pendpt) {
          // Do not flip a segment.
          if (!issubseg(searchtet)) {
            n = removeedgebyflips(&searchtet, &fc);
            if (n == 2) {
              reduceflag = 1;
            }
          }
        } else {
          assert(0); // A plc problem.
        }
      } else {
        // The edge has been flipped.
        reduceflag = 1;
      }
      if (reduceflag) {
        count++;
        // Move the last vertex into this slot.
        j = endptlist->objects - 1;
        parypt = (point *) fastlookup(endptlist, j);
        *pendpt = *parypt;
        endptlist->objects--;
        i--;
      }
    } // i

    if (count == 0) {
      // No edge is reduced.
      break;
    }

  } // while (1)

  return (int) endptlist->objects;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removevertexbyflips()    Remove a vertex by flips.                        //
//                                                                           //
// This routine attempts to remove the given vertex 'rempt' (p) from the     //
// tetrahedralization (T) by a sequence of flips.                            //
//                                                                           //
// The algorithm used here is a simple edge reduce method. Suppose there are //
// n edges connected at p. We try to reduce the number of edges by flipping  //
// any edge (not a segment) that is connecting at p.                         //
//                                                                           //
// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' //
// can be successfully removed.                                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::removevertexbyflips(point steinerpt)
{
  triface *fliptets = nullptr, wrktets[4];
  triface searchtet, spintet, neightet;
  face parentsh, spinsh, checksh;
  face leftseg, rightseg, checkseg;
  point lpt = nullptr, rpt = nullptr, apexpt; //, *parypt;
  flipconstraints fc;
  enum verttype vt;
  enum locateresult loc;
  int valence, removeflag;
  int slawson;
  int t1ver;
  int n, i;

  vt = pointtype(steinerpt);

  if (vt == FREESEGVERTEX) {
    sdecode(point2sh(steinerpt), leftseg);
    assert(leftseg.sh != nullptr);
    leftseg.shver = 0;
    if (sdest(leftseg) == steinerpt) {
      senext(leftseg, rightseg);
      spivotself(rightseg);
      assert(rightseg.sh != nullptr);
      rightseg.shver = 0;
      assert(sorg(rightseg) == steinerpt);
    } else {
      assert(sorg(leftseg) == steinerpt);
      rightseg = leftseg;
      senext2(rightseg, leftseg);
      spivotself(leftseg);
      assert(leftseg.sh != nullptr);
      leftseg.shver = 0;
      assert(sdest(leftseg) == steinerpt);
    }
    lpt = sorg(leftseg);
    rpt = sdest(rightseg);
  } else {
    // It is not a Steiner point.
    return 0;
  }

  // Try to reduce the number of edges at 'p' by flips.
  getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, nullptr);
  cavetetlist->restart(); // This list may be re-used.
  if (cavetetvertlist->objects > 3l) {
    valence = reduceedgesatvertex(steinerpt, cavetetvertlist);
  } else {
    valence = cavetetvertlist->objects;
  }
  assert(cavetetlist->objects == 0l);
  cavetetvertlist->restart();

  removeflag = 0;

  if (valence == 4) {
    // Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4
    //   vertices. This case is due to that 'p' is not exactly on the segment.
    point2tetorg(steinerpt, searchtet);
    loc = INTETRAHEDRON;
    removeflag = 1;
  } else if (valence == 5) {
    // There are 5 edges.
    if (vt == FREESEGVERTEX) {
      sstpivot1(leftseg, searchtet);
      if (org(searchtet) != steinerpt) {
        esymself(searchtet);
      }
      assert(org(searchtet) == steinerpt);
      assert(dest(searchtet) == lpt);
      i = 0; // Count the numbe of tet at the edge [p,lpt].
      neightet.tet = nullptr; // Init the face.
      spintet = searchtet;
      while (1) {
        i++;
        if (apex(spintet) == rpt) {
          // Remember the face containing the edge [lpt, rpt].
          neightet = spintet;
        }
        fnextself(spintet);
        if (spintet.tet == searchtet.tet) break;
      }
      if (i == 3) {
        // This case has been checked below.
      } else if (i == 4) {
        // There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing
        //   at [p,rpt].  There must be a face [p, lpt, rpt].  
        if (apex(neightet) == rpt) {
          // The edge (segment) has been already recovered!  
          // Check if a 6-to-2 flip is possible (to remove 'p').
          // Let 'searchtet' be [p,d,a,b]
          esym(neightet, searchtet);
          enextself(searchtet);
          // Check if there are exactly three tets at edge [p,d].
          wrktets[0] = searchtet; // [p,d,a,b]
          for (i = 0; i < 2; i++) {
            fnext(wrktets[i], wrktets[i+1]); // [p,d,b,c], [p,d,c,a]
          }
          if (apex(wrktets[0]) == oppo(wrktets[2])) {
            loc = ONFACE;
            removeflag = 1;
          }
        }
      }
    } else if (vt == FREEFACETVERTEX) {
      // It is possible to do a 6-to-2 flip to remove the vertex.
      point2tetorg(steinerpt, searchtet);
      // Get the three faces of 'searchtet' which share at p.
      //    All faces has p as origin.
      wrktets[0] = searchtet;
      wrktets[1] = searchtet;
      esymself(wrktets[1]);
      enextself(wrktets[1]);
      wrktets[2] = searchtet;
      eprevself(wrktets[2]);
      esymself(wrktets[2]);
      // All internal edges of the six tets have valance either 3 or 4.
      // Get one edge which has valance 3.
      searchtet.tet = nullptr;
      for (i = 0; i < 3; i++) {
        spintet = wrktets[i];
        valence = 0;
        while (1) {
          valence++;
          fnextself(spintet);
          if (spintet.tet == wrktets[i].tet) break;
        }
        if (valence == 3) {
          // Found the edge.
          searchtet = wrktets[i];
          break;
        } else {
          assert(valence == 4);
        }
      }
      assert(searchtet.tet != nullptr);
      // Note, we do not detach the three subfaces at p.
      // They will be removed within a 4-to-1 flip.
      loc = ONFACE;
      removeflag = 1;
    } else {
      // assert(0); DEBUG IT
    }
    //removeflag = 1;
  } 

  if (!removeflag) {
    if (vt == FREESEGVERTEX) { 
      // Check is it possible to recover the edge [lpt,rpt].
      // The condition to check is:  Whether each tet containing 'leftseg' is
      //   adjacent to a tet containing 'rightseg'.
      sstpivot1(leftseg, searchtet);
      if (org(searchtet) != steinerpt) {
        esymself(searchtet);
      }
      assert(org(searchtet) == steinerpt);
      assert(dest(searchtet) == lpt);
      spintet = searchtet;
      while (1) {
        // Go to the bottom face of this tet.
        eprev(spintet, neightet);
        esymself(neightet);  // [steinerpt, p1, p2, lpt]
        // Get the adjacent tet.
        fsymself(neightet);  // [p1, steinerpt, p2, rpt]
        if (oppo(neightet) != rpt) {
          // Found a non-matching adjacent tet.
          break;
        }
        fnextself(spintet);
        if (spintet.tet == searchtet.tet) {
          // 'searchtet' is [p,d,p1,p2].
          loc = ONEDGE;
          removeflag = 1;
          break;
        }
      }
    } // if (vt == FREESEGVERTEX)
  }

  if (!removeflag) {
    if (vt == FREESEGVERTEX) {
      // Check if the edge [lpt, rpt] exists.
      if (getedge(lpt, rpt, &searchtet)) {
        // We have recovered this edge. Shift the vertex into the volume.
        // We can recover this edge if the subfaces are not recovered yet.
        if (!checksubfaceflag) {
          // Remove the vertex from the surface mesh.
          //   This will re-create the segment [lpt, rpt] and re-triangulate
          //   all the facets at the segment.
          // Detach the subsegments from their surronding tets.
          for (i = 0; i < 2; i++) {
            checkseg = (i == 0) ? leftseg : rightseg;
            sstpivot1(checkseg, neightet);
            spintet = neightet;
            while (1) {
              tssdissolve1(spintet);
              fnextself(spintet);
              if (spintet.tet == neightet.tet) break;
            }
            sstdissolve1(checkseg);
          } // i
          slawson = 1; // Do lawson flip after removal.
          spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
          sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
          // Clear the list for new subfaces.
          caveshbdlist->restart();
          // Insert the new segment.
          assert(org(searchtet) == lpt);
          assert(dest(searchtet) == rpt);
          sstbond1(rightseg, searchtet);
          spintet = searchtet;
          while (1) {
            tsspivot1(spintet, checkseg); // FOR DEBUG ONLY
            assert(checkseg.sh == nullptr);  // FOR DEBUG ONLY
            tssbond1(spintet, rightseg);
            fnextself(spintet);
            if (spintet.tet == searchtet.tet) break;
          }
          // The Steiner point has been shifted into the volume.
          setpointtype(steinerpt, FREEVOLVERTEX);          
          st_segref_count--;
          st_volref_count++;
          return 1;
        } // if (!checksubfaceflag)
      } // if (getedge(...))
    } // if (vt == FREESEGVERTEX)
  } // if (!removeflag)

  if (!removeflag) {
    return 0;
  }

  assert(org(searchtet) == steinerpt);

  if (vt == FREESEGVERTEX) {
    // Detach the subsegments from their surronding tets.
    for (i = 0; i < 2; i++) {
      checkseg = (i == 0) ? leftseg : rightseg;
      sstpivot1(checkseg, neightet);
      spintet = neightet;
      while (1) {
        tssdissolve1(spintet);
        fnextself(spintet);
        if (spintet.tet == neightet.tet) break;
      }
      sstdissolve1(checkseg);
    } // i
    if (checksubfaceflag) {
      // Detach the subfaces at the subsegments from their attached tets.
      for (i = 0; i < 2; i++) {
        checkseg = (i == 0) ? leftseg : rightseg;
        spivot(checkseg, parentsh);
        if (parentsh.sh != nullptr) {
          spinsh = parentsh;
          while (1) {
            stpivot(spinsh, neightet);
            if (neightet.tet != nullptr) {
              tsdissolve(neightet);
            }
            sesymself(spinsh);
            stpivot(spinsh, neightet);
            if (neightet.tet != nullptr) {
              tsdissolve(neightet);
            }
            stdissolve(spinsh);
            spivotself(spinsh); // Go to the next subface.
            if (spinsh.sh == parentsh.sh) break;
          }
        }
      } // i
    } // if (checksubfaceflag)
  }

  if (loc == INTETRAHEDRON) {
    // Collect the four tets containing 'p'.
    fliptets = new triface[4];
    fliptets[0] = searchtet; // [p,d,a,b]
    for (i = 0; i < 2; i++) {
      fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
    }
    eprev(fliptets[0], fliptets[3]);
    fnextself(fliptets[3]); // it is [a,p,b,c]
    eprevself(fliptets[3]);
    esymself(fliptets[3]); // [a,b,c,p].
    // Remove p by a 4-to-1 flip.
    //flip41(fliptets, 1, 0, 0);
    flip41(fliptets, 1, &fc);
    //recenttet = fliptets[0];
  } else if (loc == ONFACE) {
    // Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in
    //   face [a,b,c].  Let 'searchtet' be the tet [p,d,a,b].
    // Collect the six tets containing 'p'.
    fliptets = new triface[6];
    fliptets[0] = searchtet; // [p,d,a,b]
    for (i = 0; i < 2; i++) {
      fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
    }
    eprev(fliptets[0], fliptets[3]);
    fnextself(fliptets[3]); // [a,p,b,e]
    esymself(fliptets[3]);  // [p,a,e,b]
    eprevself(fliptets[3]); // [e,p,a,b]
    for (i = 3; i < 5; i++) {
      fnext(fliptets[i], fliptets[i+1]); // [e,p,b,c], [e,p,c,a]
    }
    if (vt == FREEFACETVERTEX) {
      // We need to determine the location of three subfaces at p.
      valence = 0; // Re-use it.
      // Check if subfaces are all located in the lower three tets.
      //   i.e., [e,p,a,b], [e,p,b,c], and [e,p,c,a].
      for (i = 3; i < 6; i++) {
        if (issubface(fliptets[i])) valence++;
      }
      if (valence > 0) {
        assert(valence == 2);
        // We must do 3-to-2 flip in the upper part. We simply re-arrange
        //   the six tets.
        for (i = 0; i < 3; i++) {
          esym(fliptets[i+3], wrktets[i]);
          esym(fliptets[i], fliptets[i+3]);
          fliptets[i] = wrktets[i];
        }
        // Swap the last two pairs, i.e., [1]<->[[2], and [4]<->[5]
        wrktets[1] = fliptets[1];
        fliptets[1] = fliptets[2];
        fliptets[2] = wrktets[1];
        wrktets[1] = fliptets[4];
        fliptets[4] = fliptets[5];
        fliptets[5] = wrktets[1];
      }
    }
    // Remove p by a 6-to-2 flip, which is a combination of two flips:
    //   a 3-to-2 (deletes the edge [e,p]), and
    //   a 4-to-1 (deletes the vertex p).
    // First do a 3-to-2 flip on [e,p,a,b],[e,p,b,c],[e,p,c,a]. It creates
    //   two new tets: [a,b,c,p] and [b,a,c,e].  The new tet [a,b,c,p] is
    //   degenerate (has zero volume). It will be deleted in the followed
    //   4-to-1 flip.
    //flip32(&(fliptets[3]), 1, 0, 0);
    flip32(&(fliptets[3]), 1, &fc);
    // Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p].
    //   This creates a new tet [a,b,c,d].
    //flip41(fliptets, 1, 0, 0);
    flip41(fliptets, 1, &fc);
    //recenttet = fliptets[0];
  } else if (loc == ONEDGE) {
    // Let the original edge be [e,d] and p is in [e,d]. Assume there are n
    //   tets sharing at edge [e,d] originally.  We number the link vertices
    //   of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1].
    // Count the number of tets at edge [e,p] and [p,d] (this is n).
    n = 0;
    spintet = searchtet;
    while (1) {
      n++;
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break;
    }
    assert(n >= 3);
    // Collect the 2n tets containing 'p'.
    fliptets = new triface[2 * n];
    fliptets[0] = searchtet; // [p,b,p_0,p_1] 
    for (i = 0; i < (n - 1); i++) {
      fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1].
    }
    eprev(fliptets[0], fliptets[n]);
    fnextself(fliptets[n]); // [p_0,p,p_1,e]
    esymself(fliptets[n]);  // [p,p_0,e,p_1]
    eprevself(fliptets[n]); // [e,p,p_0,p_1]
    for (i = n; i <  (2 * n - 1); i++) {
      fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1].
    }
    // Remove p by a 2n-to-n flip, it is a sequence of n flips:
    // - Do a 2-to-3 flip on 
    //     [p_0,p_1,p,d] and 
    //     [p,p_1,p_0,e].
    //   This produces: 
    //     [e,d,p_0,p_1], 
    //     [e,d,p_1,p] (degenerated), and 
    //     [e,d,p,p_0] (degenerated).
    wrktets[0] = fliptets[0]; // [p,d,p_0,p_1]
    eprevself(wrktets[0]);    // [p_0,p,d,p_1]
    esymself(wrktets[0]);     // [p,p_0,p_1,d]
    enextself(wrktets[0]);    // [p_0,p_1,p,d] [0]
    wrktets[1] = fliptets[n]; // [e,p,p_0,p_1]
    enextself(wrktets[1]);    // [p,p_0,e,p_1]
    esymself(wrktets[1]);     // [p_0,p,p_1,e]
    eprevself(wrktets[1]);    // [p_1,p_0,p,e] [1]
    //flip23(wrktets, 1, 0, 0);
    flip23(wrktets, 1, &fc);
    // Save the new tet [e,d,p,p_0] (degenerated).
    fliptets[n] = wrktets[2];
    // Save the new tet [e,d,p_0,p_1].
    fliptets[0] = wrktets[0];
    // - Repeat from i = 1 to n-2: (n - 2) flips
    //   - Do a 3-to-2 flip on 
    //       [p,p_i,d,e], 
    //       [p,p_i,e,p_i+1], and 
    //       [p,p_i,p_i+1,d]. 
    //     This produces: 
    //       [d,e,p_i+1,p_i], and
    //       [e,d,p_i+1,p] (degenerated).
    for (i = 1; i < (n - 1); i++) {
      wrktets[0] = wrktets[1]; // [e,d,p_i,p] (degenerated).
      enextself(wrktets[0]);   // [d,p_i,e,p] (...)
      esymself(wrktets[0]);    // [p_i,d,p,e] (...) 
      eprevself(wrktets[0]);   // [p,p_i,d,e] (degenerated) [0].
      wrktets[1] = fliptets[n+i];  // [e,p,p_i,p_i+1]
      enextself(wrktets[1]);       // [p,p_i,e,p_i+1] [1]
      wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1]
      eprevself(wrktets[2]);    // [p_i,p,d,p_i+1]
      esymself(wrktets[2]);     // [p,p_i,p_i+1,d] [2]
      //flip32(wrktets, 1, 0, 0);
      flip32(wrktets, 1, &fc);
      // Save the new tet [e,d,p_i,p_i+1].         // FOR DEBUG ONLY
      fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY
      esymself(fliptets[i]);    // [e,d,p_i,p_i+1] // FOR DEBUG ONLY
    }
    // - Do a 4-to-1 flip on 
    //     [p,p_0,e,d],     [d,e,p_0,p],
    //     [p,p_0,d,p_n-1], [e,p_n-1,p_0,p], 
    //     [p,p_0,p_n-1,e], [p_0,p_n-1,d,p], and
    //     [e,d,p_n-1,p]. 
    //   This produces 
    //     [e,d,p_n-1,p_0] and 
    //     deletes p.
    wrktets[3] = wrktets[1];  // [e,d,p_n-1,p] (degenerated) [3]
    wrktets[0] = fliptets[n]; // [e,d,p,p_0] (degenerated)
    eprevself(wrktets[0]);    // [p,e,d,p_0] (...)
    esymself(wrktets[0]);     // [e,p,p_0,d] (...)
    enextself(wrktets[0]);    // [p,p_0,e,d] (degenerated) [0]
    wrktets[1] = fliptets[n-1];   // [p,d,p_n-1,p_0]
    esymself(wrktets[1]);         // [d,p,p_0,p_n-1]
    enextself(wrktets[1]);        // [p,p_0,d,p_n-1] [1]
    wrktets[2] = fliptets[2*n-1]; // [e,p,p_n-1,p_0]
    enextself(wrktets[2]);        // [p_p_n-1,e,p_0]
    esymself(wrktets[2]);         // [p_n-1,p,p_0,e]
    enextself(wrktets[2]);        // [p,p_0,p_n-1,e] [2]
    //flip41(wrktets, 1, 0, 0);
    flip41(wrktets, 1, &fc);
    // Save the new tet [e,d,p_n-1,p_0]             // FOR DEBUG ONLY
    fliptets[n-1] = wrktets[0];  // [e,d,p_n-1,p_0] // FOR DEBUG ONLY
    //recenttet = fliptets[0];
  } else {
    assert(0); // Unknown location.
  } // if (iloc == ...)

  delete [] fliptets;

  if (vt == FREESEGVERTEX) {
    // Remove the vertex from the surface mesh.
    //   This will re-create the segment [lpt, rpt] and re-triangulate
    //   all the facets at the segment.
    // Only do lawson flip when subfaces are not recovery yet.
    slawson = (checksubfaceflag ? 0 : 1);
    spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
    sremovevertex(steinerpt, &parentsh, &rightseg, slawson);

    // The original segment is returned in 'rightseg'. 
    rightseg.shver = 0;
    assert(sorg(rightseg) == lpt);
    assert(sdest(rightseg) == rpt);

    // Insert the new segment.
    point2tetorg(lpt, searchtet);
    finddirection(&searchtet, rpt);
    assert(dest(searchtet) == rpt);
    sstbond1(rightseg, searchtet);
    spintet = searchtet;
    while (1) {
      tsspivot1(spintet, checkseg); // FOR DEBUG ONLY
      assert(checkseg.sh == nullptr);  // FOR DEBUG ONLY
      tssbond1(spintet, rightseg);
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break;
    }

    if (checksubfaceflag) {
      // Insert subfaces at segment [lpt,rpt] into the tetrahedralization.
      spivot(rightseg, parentsh);
      if (parentsh.sh != nullptr) {
        spinsh = parentsh;
        while (1) {
          if (sorg(spinsh) != lpt) {
            sesymself(spinsh);
            assert(sorg(spinsh) == lpt);
          }
          assert(sdest(spinsh) == rpt);
          apexpt = sapex(spinsh);
          // Find the adjacent tet of [lpt,rpt,apexpt];
          spintet = searchtet;
          while (1) {
            if (apex(spintet) == apexpt) {
              tsbond(spintet, spinsh);
              sesymself(spinsh); // Get to another side of this face.
              fsym(spintet, neightet);
              tsbond(neightet, spinsh);
              sesymself(spinsh); // Get back to the original side.
              break;
            }
            fnextself(spintet);
            assert(spintet.tet != searchtet.tet);
            //if (spintet.tet == searchtet.tet) break;
          }
          spivotself(spinsh);
          if (spinsh.sh == parentsh.sh) break;
        }
      }
    } // if (checksubfaceflag)

    // Clear the set of new subfaces.
    caveshbdlist->restart();
  } // if (vt == FREESEGVERTEX)

  // The point has been removed.
  if (pointtype(steinerpt) != UNUSEDVERTEX) {
    setpointtype(steinerpt, UNUSEDVERTEX);
    unuverts++;
  }
  // Update the correspinding counters.
  if (vt == FREESEGVERTEX) {
    st_segref_count--;
  } else if (vt == FREEFACETVERTEX) {
    st_facref_count--;
  } else if (vt == FREEVOLVERTEX) {
    st_volref_count--;
  }
  if (steinerleft > 0) steinerleft++;

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppressbdrysteinerpoint()    Suppress a boundary Steiner point           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::suppressbdrysteinerpoint(point steinerpt)
{
  face parentsh, spinsh, *parysh;
  face leftseg, rightseg;
  point lpt = nullptr, rpt = nullptr;
  int i;

  verttype vt = pointtype(steinerpt);

  if (vt == FREESEGVERTEX) {
    sdecode(point2sh(steinerpt), leftseg);
    leftseg.shver = 0;
    if (sdest(leftseg) == steinerpt) {
      senext(leftseg, rightseg);
      spivotself(rightseg);
      assert(rightseg.sh != nullptr);
      rightseg.shver = 0;
      assert(sorg(rightseg) == steinerpt);
    } else {
      assert(sorg(leftseg) == steinerpt);
      rightseg = leftseg;
      senext2(rightseg, leftseg);
      spivotself(leftseg);
      assert(leftseg.sh != nullptr);
      leftseg.shver = 0;
      assert(sdest(leftseg) == steinerpt);
    }
    lpt = sorg(leftseg);
    rpt = sdest(rightseg);
    if (b->verbose > 2) {
      printf("      Suppressing Steiner point %d in segment (%d, %d).\n",
             pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
    }
    // Get all subfaces at the left segment [lpt, steinerpt].
    spivot(leftseg, parentsh);
    spinsh = parentsh;
    while (1) {
      cavesegshlist->newindex((void **) &parysh);
      *parysh = spinsh;
      // Orient the face consistently. 
      if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh);
      spivotself(spinsh);
      if (spinsh.sh == nullptr) break;
      if (spinsh.sh == parentsh.sh) break;
    }
    if (cavesegshlist->objects < 2) {
      // It is a single segment. Not handle it yet.
      cavesegshlist->restart();
      return 0;
    }
  } else if (vt == FREEFACETVERTEX) {
    if (b->verbose > 2) {
      printf("      Suppressing Steiner point %d from facet.\n",
             pointmark(steinerpt));
    }
    sdecode(point2sh(steinerpt), parentsh);
    // A facet Steiner point. There are exactly two sectors.
    for (i = 0; i < 2; i++) {
      cavesegshlist->newindex((void **) &parysh);
      *parysh = parentsh;
      sesymself(parentsh);
    }
  } else {
    return 0;
  }

  triface searchtet, neightet, *parytet;
  point pa, pb, pc, pd;
  REAL v1[3], v2[3], len, u;

  REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,};
  REAL ori, minvol, smallvol;
  int samplesize;
  int it, j, k;

  int n = (int) cavesegshlist->objects;
  point *newsteiners = new point[n];
  for (i = 0; i < n; i++) newsteiners[i] = nullptr;

  // Search for each sector an interior vertex. 
  for (i = 0; i < cavesegshlist->objects; i++) {
    parysh = (face *) fastlookup(cavesegshlist, i);
    stpivot(*parysh, searchtet);
    // Skip it if it is outside.
    if (ishulltet(searchtet)) continue;
    // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as
    //   opposite.  Subfaces in 'caveshlist' all contain 'steinerpt' as apex.
    //   Moreover, subfaces are oriented towards the interior of the ball.
    setpoint2tet(steinerpt, encode(searchtet));
    getvertexstar(0, steinerpt, cavetetlist, nullptr, caveshlist);
    // Calculate the searching vector.
    pa = sorg(*parysh);
    pb = sdest(*parysh);
    pc = sapex(*parysh);
    facenormal(pa, pb, pc, v1, 1, nullptr);
    len = sqrt(dot(v1, v1));
    assert(len > 0.0);
    v1[0] /= len;
    v1[1] /= len;
    v1[2] /= len;
    if (vt == FREESEGVERTEX) {
      parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n);
      pd = sapex(*parysh);
      facenormal(pb, pa, pd, v2, 1, nullptr);
      len = sqrt(dot(v2, v2));
      assert(len > 0.0);
      v2[0] /= len;
      v2[1] /= len;
      v2[2] /= len;
      // Average the two vectors.
      v1[0] = 0.5 * (v1[0] + v2[0]);
      v1[1] = 0.5 * (v1[1] + v2[1]);
      v1[2] = 0.5 * (v1[2] + v2[2]);
    }
    // Search the intersection of the ray starting from 'steinerpt' to
    //   the search direction 'v1' and the shell of the half-ball.
    // - Construct an endpoint.
    len = distance(pa, pb);
    v2[0] = steinerpt[0] + len * v1[0];
    v2[1] = steinerpt[1] + len * v1[1];
    v2[2] = steinerpt[2] + len * v1[2];
    for (j = 0; j < cavetetlist->objects; j++) {
      parytet = (triface *) fastlookup(cavetetlist, j);
      pa = org(*parytet);
      pb = dest(*parytet);
      pc = apex(*parytet);
      // Test if the ray startpt->v2 lies in the cone: where 'steinerpt'
      //   is the apex, and three sides are defined by the triangle 
      //   [pa, pb, pc].
      ori = orient3d(steinerpt, pa, pb, v2);
      if (ori >= 0) {
        ori = orient3d(steinerpt, pb, pc, v2);
        if (ori >= 0) {
          ori = orient3d(steinerpt, pc, pa, v2);
          if (ori >= 0) {
            // Found! Calculate the intersection.
            planelineint(pa, pb, pc, steinerpt, v2, startpt, &u);
            assert(u != 0.0);
            break;
          }
        }
      }
    } // j
    assert(j < cavetetlist->objects); // There must be an intersection.
    // Close the ball by adding the subfaces.
    for (j = 0; j < caveshlist->objects; j++) {
      parysh = (face *) fastlookup(caveshlist, j);
      stpivot(*parysh, neightet);
      cavetetlist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    // Search a best point inside the segment [startpt, steinerpt].
    it = 0;
    samplesize = 100;
    v1[0] = steinerpt[0] - startpt[0];
    v1[1] = steinerpt[1] - startpt[1];
    v1[2] = steinerpt[2] - startpt[2];
    minvol = -1.0;
    while (it < 3) {
      for (j = 1; j < samplesize - 1; j++) {
        samplept[0] = startpt[0] + ((REAL) j / (REAL) samplesize) * v1[0];
        samplept[1] = startpt[1] + ((REAL) j / (REAL) samplesize) * v1[1];
        samplept[2] = startpt[2] + ((REAL) j / (REAL) samplesize) * v1[2];
        // Find the minimum volume for 'samplept'.
        smallvol = -1;
        for (k = 0; k < cavetetlist->objects; k++) {
          parytet = (triface *) fastlookup(cavetetlist, k);
          pa = org(*parytet);
          pb = dest(*parytet);
          pc = apex(*parytet);
          ori = orient3d(pb, pa, pc, samplept);
          if (ori <= 0) {
            break; // An invalid tet.
          }
          if (smallvol == -1) {
            smallvol = ori;
          } else {
            if (ori < smallvol) smallvol = ori;
          }
        } // k
        if (k == cavetetlist->objects) {
          // Found a valid point. Remember it.
          if (minvol == -1.0) {
            candpt[0] = samplept[0];
            candpt[1] = samplept[1];
            candpt[2] = samplept[2];
            minvol = smallvol;
          } else {
            if (minvol < smallvol) {
              // It is a better location. Remember it.
              candpt[0] = samplept[0];
              candpt[1] = samplept[1];
              candpt[2] = samplept[2];
              minvol = smallvol;
            } else {
              // No improvement of smallest volume. 
              // Since we are searching along the line [startpt, steinerpy],
              // The smallest volume can only be decreased later.
              break;
            }
          }
        }
      } // j
      if (minvol > 0) break; 
      samplesize *= 10;
      it++;
    } // while (it < 3)
    if (minvol == -1.0) {
      // Failed to find a valid point.
      cavetetlist->restart();
      caveshlist->restart();
      break;
    }
    // Create a new Steiner point inside this section.
    makepoint(&(newsteiners[i]), FREEVOLVERTEX);
    newsteiners[i][0] = candpt[0];
    newsteiners[i][1] = candpt[1];
    newsteiners[i][2] = candpt[2];
    cavetetlist->restart();
    caveshlist->restart();
  } // i

  if (i < cavesegshlist->objects) {
    // Failed to suppress the vertex.
    for (; i > 0; i--) {
      if (newsteiners[i - 1] != nullptr) {
        pointdealloc(newsteiners[i - 1]);
      }
    }
    delete [] newsteiners;
    cavesegshlist->restart();
    return 0;
  }

  // Remove p from the segment or the facet.
  triface newtet, newface, spintet;
  face newsh, neighsh;
  face *splitseg, checkseg;
  int slawson = 0; // Do not do flip afterword.
  int t1ver;

  if (vt == FREESEGVERTEX) {
    // Detach 'leftseg' and 'rightseg' from their adjacent tets.
    //   These two subsegments will be deleted. 
    sstpivot1(leftseg, neightet);
    spintet = neightet;
    while (1) {
      tssdissolve1(spintet);
      fnextself(spintet);
      if (spintet.tet == neightet.tet) break;
    }
    sstpivot1(rightseg, neightet);
    spintet = neightet;
    while (1) {
      tssdissolve1(spintet);
      fnextself(spintet);
      if (spintet.tet == neightet.tet) break;
    }
  }

  // Loop through all sectors bounded by facets at this segment.
  //   Within each sector, create a new Steiner point 'np', and replace 'p'
  //   by 'np' for all tets in this sector.
  for (i = 0; i < cavesegshlist->objects; i++) {
    parysh = (face *) fastlookup(cavesegshlist, i);
    // 'parysh' is the face [lpt, steinerpt, #].
    stpivot(*parysh, neightet);
    // Get all tets in this sector.
    setpoint2tet(steinerpt, encode(neightet));
    getvertexstar(0, steinerpt, cavetetlist, nullptr, caveshlist);
    if (!ishulltet(neightet)) {
      // Within each tet in the ball, replace 'p' by 'np'.
      for (j = 0; j < cavetetlist->objects; j++) {
        parytet = (triface *) fastlookup(cavetetlist, j);
        setoppo(*parytet, newsteiners[i]);
      } // j
      // Point to a parent tet.
      parytet = (triface *) fastlookup(cavetetlist, 0);
      setpoint2tet(newsteiners[i], (tetrahedron) (parytet->tet)); 
      st_volref_count++;
      if (steinerleft > 0) steinerleft--;
    }
    // Disconnect the set of boundary faces. They're temporarily open faces.
    //   They will be connected to the new tets after 'p' is removed.
    for (j = 0; j < caveshlist->objects; j++) {
      // Get a boundary face.
      parysh = (face *) fastlookup(caveshlist, j);
      stpivot(*parysh, neightet);
      //assert(apex(neightet) == newpt);
      // Clear the connection at this face.
      dissolve(neightet);
      tsdissolve(neightet);
    }
    // Clear the working lists.
    cavetetlist->restart();
    caveshlist->restart();
  } // i
  cavesegshlist->restart();

  if (vt == FREESEGVERTEX) { 
    spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
    splitseg = &rightseg;
  } else {
    if (sdest(parentsh) == steinerpt) {
      senextself(parentsh);
    } else if (sapex(parentsh) == steinerpt) {
      senext2self(parentsh);
    }
    assert(sorg(parentsh) == steinerpt);
    splitseg = nullptr;
  }
  sremovevertex(steinerpt, &parentsh, splitseg, slawson);

  if (vt == FREESEGVERTEX) {
    // The original segment is returned in 'rightseg'. 
    rightseg.shver = 0;
  }

  // For each new subface, create two new tets at each side of it.
  //   Both of the two new tets have its opposite be dummypoint. 
  for (i = 0; i < caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    sinfect(*parysh); // Mark it for connecting new tets.
    newsh = *parysh;
    pa = sorg(newsh);
    pb = sdest(newsh);
    pc = sapex(newsh);
    maketetrahedron(&newtet);
    maketetrahedron(&neightet);
    setvertices(newtet, pa, pb, pc, dummypoint);
    setvertices(neightet, pb, pa, pc, dummypoint);
    bond(newtet, neightet);
    tsbond(newtet, newsh);
    sesymself(newsh);
    tsbond(neightet, newsh);
  }
  // Temporarily increase the hullsize.
  hullsize += (caveshbdlist->objects * 2l);

  if (vt == FREESEGVERTEX) {
    // Connecting new tets at the recovered segment.
    spivot(rightseg, parentsh);
    assert(parentsh.sh != nullptr);
    spinsh = parentsh;
    while (1) {
      if (sorg(spinsh) != lpt) sesymself(spinsh);
      // Get the new tet at this subface.
      stpivot(spinsh, newtet);
      tssbond1(newtet, rightseg);
      // Go to the other face at this segment.
      spivot(spinsh, neighsh);
      if (sorg(neighsh) != lpt) sesymself(neighsh);
      sesymself(neighsh);
      stpivot(neighsh, neightet);
      tssbond1(neightet, rightseg);
      sstbond1(rightseg, neightet); 
      // Connecting two adjacent tets at this segment.
      esymself(newtet);
      esymself(neightet);
      // Connect the two tets (at rightseg) together.
      bond(newtet, neightet);
      // Go to the next subface.
      spivotself(spinsh);
      if (spinsh.sh == parentsh.sh) break;
    }
  }

  // Connecting new tets at new subfaces together.
  for (i = 0; i < caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    newsh = *parysh;
    //assert(sinfected(newsh));
    // Each new subface contains two new tets.
    for (k = 0; k < 2; k++) {
      stpivot(newsh, newtet);
      for (j = 0; j < 3; j++) {
        // Check if this side is open.
        esym(newtet, newface);
        if (newface.tet[newface.ver & 3] == nullptr) {
          // An open face. Connect it to its adjacent tet.
          sspivot(newsh, checkseg);
          if (checkseg.sh != nullptr) {
            // A segment. It must not be the recovered segment.
            tssbond1(newtet, checkseg);
            sstbond1(checkseg, newtet);
          }
          spivot(newsh, neighsh);
          if (neighsh.sh != nullptr) {
            // The adjacent subface exists. It's not a dangling segment.
            if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh);
            stpivot(neighsh, neightet);
            if (sinfected(neighsh)) {
              esymself(neightet);
              assert(neightet.tet[neightet.ver & 3] == nullptr);          
            } else {
              // Search for an open face at this edge.
              spintet = neightet;
              while (1) {
                esym(spintet, searchtet);
                fsym(searchtet, spintet);
                if (spintet.tet == nullptr) break;
                assert(spintet.tet != neightet.tet);
              }
              // Found an open face at 'searchtet'.
              neightet = searchtet;
            }
          } else {
            // The edge (at 'newsh') is a dangling segment.
            assert(checkseg.sh != nullptr);
            // Get an adjacent tet at this segment.
            sstpivot1(checkseg, neightet);
            assert(!isdeadtet(neightet));
            if (org(neightet) != sdest(newsh)) esymself(neightet);
            assert((org(neightet) == sdest(newsh)) &&
                   (dest(neightet) == sorg(newsh)));
            // Search for an open face at this edge.
            spintet = neightet;
            while (1) {
              esym(spintet, searchtet);
              fsym(searchtet, spintet);
              if (spintet.tet == nullptr) break;
              assert(spintet.tet != neightet.tet);
            }
            // Found an open face at 'searchtet'.
            neightet = searchtet;
          }
          pc = apex(newface);
          if (apex(neightet) == steinerpt) {
            // Exterior case. The 'neightet' is a hull tet which contain
            //   'steinerpt'. It will be deleted after 'steinerpt' is removed. 
            assert(pc == dummypoint);
            caveoldtetlist->newindex((void **) &parytet);
            *parytet = neightet;
            // Connect newface to the adjacent hull tet of 'neightet', which
            //   has the same edge as 'newface', and does not has 'steinerpt'.
            fnextself(neightet);
          } else {
            if (pc == dummypoint) {
              if (apex(neightet) != dummypoint) {
                setapex(newface, apex(neightet));
                // A hull tet has turned into an interior tet.
                hullsize--; // Must update the hullsize.
              } 
            }
          }
          bond(newface, neightet);
        } // if (newface.tet[newface.ver & 3] == nullptr)
        enextself(newtet);
        senextself(newsh);
      } // j
      sesymself(newsh);
    } // k
  } // i

  // Unmark all new subfaces.
  for (i = 0; i < caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    suninfect(*parysh);
  }
  caveshbdlist->restart();

  if (caveoldtetlist->objects > 0l) {
    // Delete hull tets which contain 'steinerpt'.
    for (i = 0; i < caveoldtetlist->objects; i++) {
      parytet = (triface *) fastlookup(caveoldtetlist, i);
      tetrahedrondealloc(parytet->tet);
    }
    // Must update the hullsize.
    hullsize -= caveoldtetlist->objects;
    caveoldtetlist->restart();
  }

  setpointtype(steinerpt, UNUSEDVERTEX);
  unuverts++;
  if (vt == FREESEGVERTEX) {
    st_segref_count--;
  } else { // vt == FREEFACETVERTEX
    st_facref_count--;
  }
  if (steinerleft > 0) steinerleft++;  // We've removed a Steiner points.


  point *parypt;
  int steinercount = 0;

  int bak_fliplinklevel = b->fliplinklevel;
  b->fliplinklevel = 100000; // Unlimited flip level.

  // Try to remove newly added Steiner points.
  for (i = 0; i < n; i++) {
    if (newsteiners[i] != nullptr) {
      if (!removevertexbyflips(newsteiners[i])) {
        if (b->nobisect_param > 0) { // Not -Y0
          // Save it in subvertstack for removal.
          subvertstack->newindex((void **) &parypt);
          *parypt = newsteiners[i];
        }
        steinercount++;
      }
    }
  }

  b->fliplinklevel = bak_fliplinklevel;

  if (steinercount > 0) {
    if (b->verbose > 2) {
      printf("      Added %d interior Steiner points.\n", steinercount);
    }
  }

  delete [] newsteiners;

  return 1;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppresssteinerpoints()    Suppress Steiner points.                       //
//                                                                           //
// All Steiner points have been saved in 'subvertstack' in the routines      //
// carveholes() and suppresssteinerpoint().                                  //
// Each Steiner point is either removed or shifted into the interior.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::suppresssteinerpoints()
{

  if (!b->quiet) {
    printf("Suppressing Steiner points ...\n");
  }

  point rempt, *parypt;

  int bak_fliplinklevel = b->fliplinklevel;
  b->fliplinklevel = 100000; // Unlimited flip level.
  int suppcount = 0, remcount = 0;
  int i;

  // Try to suppress boundary Steiner points.
  for (i = 0; i < subvertstack->objects; i++) {
    parypt = (point *) fastlookup(subvertstack, i);
    rempt = *parypt;
    if (pointtype(rempt) != UNUSEDVERTEX) {
      if ((pointtype(rempt) == FREESEGVERTEX) || 
          (pointtype(rempt) == FREEFACETVERTEX)) {
        if (suppressbdrysteinerpoint(rempt)) {
          suppcount++;
        }
      }
    }
  } // i

  if (suppcount > 0) {
    if (b->verbose) {
      printf("  Suppressed %d boundary Steiner points.\n", suppcount);
    }
  }

  if (b->nobisect_param > 0) { // -Y1
    for (i = 0; i < subvertstack->objects; i++) {
      parypt = (point *) fastlookup(subvertstack, i);
      rempt = *parypt;
      if (pointtype(rempt) != UNUSEDVERTEX) {
        if (pointtype(rempt) == FREEVOLVERTEX) {
          if (removevertexbyflips(rempt)) {
            remcount++;
          }
        }
      }
    }
  }

  if (remcount > 0) {
    if (b->verbose) {
      printf("  Removed %d interior Steiner points.\n", remcount);
    }
  }

  b->fliplinklevel = bak_fliplinklevel;

  if (b->nobisect_param > 1) { // -Y2
    // Smooth interior Steiner points.
    optparameters opm;
    triface *parytet;
    point *ppt;
    REAL ori;
    int smtcount, count, ivcount;
    int nt, j;

    // Point smooth options.
    opm.max_min_volume = 1;
    opm.numofsearchdirs = 20;
    opm.searchstep = 0.001;
    opm.maxiter = 30; // Limit the maximum iterations.

    smtcount = 0;

    do {

      nt = 0;

      while (1) {
        count = 0;
        ivcount = 0; // Clear the inverted count.

        for (i = 0; i < subvertstack->objects; i++) {
          parypt = (point *) fastlookup(subvertstack, i);
          rempt = *parypt;
          if (pointtype(rempt) == FREEVOLVERTEX) {
            getvertexstar(1, rempt, cavetetlist, nullptr, nullptr);
            // Calculate the initial smallest volume (maybe zero or negative).
            for (j = 0; j < cavetetlist->objects; j++) {
              parytet = (triface *) fastlookup(cavetetlist, j);
              ppt = (point *) &(parytet->tet[4]);
              ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]);
              if (j == 0) {
                opm.initval = ori;
              } else {
                if (opm.initval > ori) opm.initval = ori; 
              }
            }
            if (smoothpoint(rempt, cavetetlist, 1, &opm)) {
              count++;
            }
            if (opm.imprval <= 0.0) {
              ivcount++; // The mesh contains inverted elements.
            }
            cavetetlist->restart();
          }
        } // i

        smtcount += count;

        if (count == 0) {
          // No point has been smoothed.
          break;
        }

        nt++;
        if (nt > 2) {
          break; // Already three iterations.
        }
      } // while

      if (ivcount > 0) {
        // There are inverted elements!
        if (opm.maxiter > 0) {
          // Set unlimited smoothing steps. Try again.
          opm.numofsearchdirs = 30;
          opm.searchstep = 0.0001;
          opm.maxiter = -1;
          continue;
        }
      }

      break;
    } while (1); // Additional loop for (ivcount > 0)

    if (ivcount > 0) {
      printf("BUG Report!  The mesh contain inverted elements.\n");
    }

    if (b->verbose) {
      if (smtcount > 0) {
        printf("  Smoothed %d Steiner points.\n", smtcount); 
      }
    }
  } // -Y2

  subvertstack->restart();

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoverboundary()    Recover segments and facets.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::recoverboundary(clock_t& tv)
{
  arraypool *misseglist, *misshlist;
  arraypool *bdrysteinerptlist;
  face searchsh, *parysh;
  face searchseg, *paryseg;
  point rempt, *parypt;
  long ms; // The number of missing segments/subfaces.
  int nit; // The number of iterations.
  int s, i;

  // Counters.
  long bak_segref_count, bak_facref_count, bak_volref_count;

  if (!b->quiet) {
    printf("Recovering boundaries...\n");
  }


  if (b->verbose) {
    printf("  Recovering segments.\n");
  }

  // Segments will be introduced.
  checksubsegflag = 1;

  misseglist = new arraypool(sizeof(face), 8);
  bdrysteinerptlist = new arraypool(sizeof(point), 8);

  // [Bruno] protected all the body of the function in
  // a big try/catch block, to make sure that dynamically
  // allocated variables are free-ed before leaving the
  // function when an exception is triggered (e.g. intersecting
  // constrained facets).
  // Note: misseglist, misshlist and bdrysteinerptlist can probably
  // be declared as local variables (instead of allocated on the heap),
  // this will make everything easier (but I do not know enough the
  // rest of the code to do that...)
  
  try {
  
  // In random order.
  subsegs->traversalinit();
  for (i = 0; i < subsegs->items; i++) {
    s = randomnation(i + 1);
    // Move the s-th seg to the i-th.
    subsegstack->newindex((void **) &paryseg);
    *paryseg = * (face *) fastlookup(subsegstack, s);
    // Put i-th seg to be the s-th.
    searchseg.sh = shellfacetraverse(subsegs);
    paryseg = (face *) fastlookup(subsegstack, s);
    *paryseg = searchseg;
  }

  // The init number of missing segments.
  ms = subsegs->items;
  nit = 0; 
  if (b->fliplinklevel < 0) {
    autofliplinklevel = 1; // Init value.
  }

  // First, trying to recover segments by only doing flips.
  while (1) {
    recoversegments(misseglist, 0, 0);

    if (misseglist->objects > 0) {
      if (b->fliplinklevel >= 0) {
        break;
      } else {
        if (misseglist->objects >= ms) {
          nit++;
          if (nit >= 3) {
            //break;
            // Do the last round with unbounded flip link level.
            b->fliplinklevel = 100000;
          }
        } else {
          ms = misseglist->objects;
          if (nit > 0) {
            nit--;
          }
        }
        for (i = 0; i < misseglist->objects; i++) {
          subsegstack->newindex((void **) &paryseg);
          *paryseg = * (face *) fastlookup(misseglist, i);
        }
        misseglist->restart();
        autofliplinklevel+=b->fliplinklevelinc;
      }
    } else {
      // All segments are recovered.
      break;
    }
  } // while (1)

  if (b->verbose) {
    printf("  %ld (%ld) segments are recovered (missing).\n", 
           subsegs->items - misseglist->objects, misseglist->objects);
  }

  if (misseglist->objects > 0) {
    // Second, trying to recover segments by doing more flips (fullsearch).
    while (misseglist->objects > 0) {
      ms = misseglist->objects;
      for (i = 0; i < misseglist->objects; i++) {
        subsegstack->newindex((void **) &paryseg);
        *paryseg = * (face *) fastlookup(misseglist, i);
      }
      misseglist->restart();

      recoversegments(misseglist, 1, 0);

      if (misseglist->objects < ms) {
        // The number of missing segments is reduced.
        continue;
      } else {
        break;
      }
    }
    if (b->verbose) {
      printf("  %ld (%ld) segments are recovered (missing).\n", 
             subsegs->items - misseglist->objects, misseglist->objects);
    }
  }

  if (misseglist->objects > 0) {
    // Third, trying to recover segments by doing more flips (fullsearch)
    //   and adding Steiner points in the volume.
    while (misseglist->objects > 0) {
      ms = misseglist->objects;
      for (i = 0; i < misseglist->objects; i++) {
        subsegstack->newindex((void **) &paryseg);
        *paryseg = * (face *) fastlookup(misseglist, i);
      }
      misseglist->restart();

      recoversegments(misseglist, 1, 1);

      if (misseglist->objects < ms) {
        // The number of missing segments is reduced.
        continue;
      } else {
        break;
      }
    }
    if (b->verbose) {
      printf("  Added %ld Steiner points in volume.\n", st_volref_count);
    }
  }

  if (misseglist->objects > 0) {
    // Last, trying to recover segments by doing more flips (fullsearch),
    //   and adding Steiner points in the volume, and splitting segments.
    long bak_inpoly_count = st_volref_count; //st_inpoly_count;
    for (i = 0; i < misseglist->objects; i++) {
      subsegstack->newindex((void **) &paryseg);
      *paryseg = * (face *) fastlookup(misseglist, i);
    }
    misseglist->restart();

    recoversegments(misseglist, 1, 2);

    if (b->verbose) {
      printf("  Added %ld Steiner points in segments.\n", st_segref_count);
      if (st_volref_count > bak_inpoly_count) {
        printf("  Added another %ld Steiner points in volume.\n", 
               st_volref_count - bak_inpoly_count);
      }
    }
    assert(misseglist->objects == 0l);
  }


  if (st_segref_count > 0) {
    // Try to remove the Steiner points added in segments.
    bak_segref_count = st_segref_count;
    bak_volref_count = st_volref_count;
    for (i = 0; i < subvertstack->objects; i++) {
      // Get the Steiner point.
      parypt = (point *) fastlookup(subvertstack, i);
      rempt = *parypt;
      if (!removevertexbyflips(rempt)) {
        // Save it in list.
        bdrysteinerptlist->newindex((void **) &parypt);
        *parypt = rempt;
      }
    }
    if (b->verbose) {
      if (st_segref_count < bak_segref_count) {
        if (bak_volref_count < st_volref_count) {
          printf("  Suppressed %ld Steiner points in segments.\n", 
                 st_volref_count - bak_volref_count);
        }
        if ((st_segref_count + (st_volref_count - bak_volref_count)) <
            bak_segref_count) {
          printf("  Removed %ld Steiner points in segments.\n", 
                 bak_segref_count - 
                   (st_segref_count + (st_volref_count - bak_volref_count)));
        }
      }
    }
    subvertstack->restart();
  }


  tv = clock();

  if (b->verbose) {
    printf("  Recovering facets.\n");
  }

  // Subfaces will be introduced.
  checksubfaceflag = 1;

  misshlist = new arraypool(sizeof(face), 8);

  // Randomly order the subfaces.
  subfaces->traversalinit();
  for (i = 0; i < subfaces->items; i++) {
    s = randomnation(i + 1);
    // Move the s-th subface to the i-th.
    subfacstack->newindex((void **) &parysh);
    *parysh = * (face *) fastlookup(subfacstack, s);
    // Put i-th subface to be the s-th.
    searchsh.sh = shellfacetraverse(subfaces);
    parysh = (face *) fastlookup(subfacstack, s);
    *parysh = searchsh;
  }

  ms = subfaces->items;
  nit = 0; 
  b->fliplinklevel = -1; // Init.
  if (b->fliplinklevel < 0) {
    autofliplinklevel = 1; // Init value.
  }

  while (1) {
    recoversubfaces(misshlist, 0);

    if (misshlist->objects > 0) {
      if (b->fliplinklevel >= 0) {
        break;
      } else {
        if (misshlist->objects >= ms) {
          nit++;
          if (nit >= 3) {
            //break;
            // Do the last round with unbounded flip link level.
            b->fliplinklevel = 100000;
          }
        } else {
          ms = misshlist->objects;
          if (nit > 0) {
            nit--;
          }
        }
        for (i = 0; i < misshlist->objects; i++) {
          subfacstack->newindex((void **) &parysh);
          *parysh = * (face *) fastlookup(misshlist, i);
        }
        misshlist->restart();
        autofliplinklevel+=b->fliplinklevelinc;
      }
    } else {
      // All subfaces are recovered.
      break;
    }
  } // while (1)

  if (b->verbose) {
    printf("  %ld (%ld) subfaces are recovered (missing).\n", 
           subfaces->items - misshlist->objects, misshlist->objects);
  }

  if (misshlist->objects > 0) {
    // There are missing subfaces. Add Steiner points.
    for (i = 0; i < misshlist->objects; i++) {
      subfacstack->newindex((void **) &parysh);
      *parysh = * (face *) fastlookup(misshlist, i);
    }
    misshlist->restart();

    recoversubfaces(nullptr, 1);

    if (b->verbose) {
      printf("  Added %ld Steiner points in facets.\n", st_facref_count);
    }
  }


  if (st_facref_count > 0) {
    // Try to remove the Steiner points added in facets.
    bak_facref_count = st_facref_count;
    for (i = 0; i < subvertstack->objects; i++) {
      // Get the Steiner point.
      parypt = (point *) fastlookup(subvertstack, i);
      rempt = *parypt;
      if (!removevertexbyflips(*parypt)) {
        // Save it in list.
        bdrysteinerptlist->newindex((void **) &parypt);
        *parypt = rempt;
      }
    }
    if (b->verbose) {
      if (st_facref_count < bak_facref_count) {
        printf("  Removed %ld Steiner points in facets.\n", 
               bak_facref_count - st_facref_count);
      }
    }
    subvertstack->restart();
  }


  if (bdrysteinerptlist->objects > 0) {
    if (b->verbose) {
      printf("  %ld Steiner points remained in boundary.\n",
             bdrysteinerptlist->objects);
    }
  } // if


  // Accumulate the dynamic memory.
  totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory +
                      bdrysteinerptlist->totalmemory);

  // [Bruno] added this catch block to delete dynamically allocated
  // variables before "re-throwing" the exception.
  } catch(...) {
      delete bdrysteinerptlist;
      delete misseglist;
      delete misshlist;
      throw;
  }
  
  delete bdrysteinerptlist;
  delete misseglist;
  delete misshlist;
}

////                                                                       ////
////                                                                       ////
//// steiner_cxx //////////////////////////////////////////////////////////////


//// reconstruct_cxx //////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carveholes()    Remove tetrahedra not in the mesh domain.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


void tetgenmesh::carveholes()
{
  arraypool *tetarray, *hullarray;
  triface tetloop, neightet, *parytet, *parytet1;
  triface *regiontets = nullptr;
  face checksh, *parysh;
  face checkseg;
  point ptloop, *parypt;
  int t1ver;
  int i, j, k;

  if (!b->quiet) {
    if (b->convex) {
      printf("Marking exterior tetrahedra ...\n");
    } else {
      printf("Removing exterior tetrahedra ...\n");
    }
  }

  // Initialize the pool of exterior tets.
  tetarray = new arraypool(sizeof(triface), 10);
  hullarray = new arraypool(sizeof(triface), 10);

  // Collect unprotected tets and hull tets.
  tetrahedrons->traversalinit();
  tetloop.ver = 11; // The face opposite to dummypoint.
  tetloop.tet = alltetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    if (ishulltet(tetloop)) {
      // Is this side protected by a subface?
      if (!issubface(tetloop)) {
        // Collect an unprotected hull tet and tet.
        infect(tetloop);
        hullarray->newindex((void **) &parytet);
        *parytet = tetloop;
        // tetloop's face number is 11 & 3 = 3.
        decode(tetloop.tet[3], neightet);
        if (!infected(neightet)) {
          infect(neightet);
          tetarray->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    }
    tetloop.tet = alltetrahedrontraverse();
  }

  if (in->numberofholes > 0) {
    // Mark as infected any tets inside volume holes.
    for (i = 0; i < 3 * in->numberofholes; i += 3) {
      // Search a tet containing the i-th hole point.
      neightet.tet = nullptr;
      randomsample(&(in->holelist[i]), &neightet);
      if (locate(&(in->holelist[i]), &neightet, 0) != OUTSIDE) {
        // The tet 'neightet' contain this point.
        if (!infected(neightet)) {
          infect(neightet);
          tetarray->newindex((void **) &parytet);
          *parytet = neightet;
          // Add its adjacent tet if it is not protected.
          if (!issubface(neightet)) {
            decode(neightet.tet[neightet.ver & 3], tetloop);
            if (!infected(tetloop)) {
              infect(tetloop);
              if (ishulltet(tetloop)) {
                hullarray->newindex((void **) &parytet);
              } else {
                tetarray->newindex((void **) &parytet);
              }
              *parytet = tetloop;
            }
          }
          else {
            // It is protected. Check if its adjacent tet is a hull tet.
            decode(neightet.tet[neightet.ver & 3], tetloop);
            if (ishulltet(tetloop)) {
              // It is hull tet, add it into the list. Moreover, the subface
              //   is dead, i.e., both sides are in exterior.
              if (!infected(tetloop)) {
                infect(tetloop);
                hullarray->newindex((void **) &parytet);
                *parytet = tetloop;
              }
            }
            if (infected(tetloop)) {
              // Both sides of this subface are in exterior.
              tspivot(neightet, checksh);
              sinfect(checksh); // Only queue it once.
              subfacstack->newindex((void **) &parysh);
              *parysh = checksh;
            }
          }
        } // if (!infected(neightet))
      } else {
        // A hole point locates outside of the convex hull.
        if (!b->quiet) {
          printf("Warning:  The %d-th hole point ", i/3 + 1);
          printf("lies outside the convex hull.\n");
        }
      }
    } // i
  } // if (in->numberofholes > 0)

  if (b->regionattrib && (in->numberofregions > 0)) { // -A option.
    // Record the tetrahedra that contains the region points for assigning
    //   region attributes after the holes have been carved.
    regiontets = new triface[in->numberofregions];
    // Mark as marktested any tetrahedra inside volume regions.
    for (i = 0; i < 5 * in->numberofregions; i += 5) {
      // Search a tet containing the i-th region point.
      neightet.tet = nullptr;
      randomsample(&(in->regionlist[i]), &neightet);
      if (locate(&(in->regionlist[i]), &neightet, 0) != OUTSIDE) {
        regiontets[i/5] = neightet;
      } else {
        if (!b->quiet) {
          printf("Warning:  The %d-th region point ", i/5+1);
          printf("lies outside the convex hull.\n");
        }
        regiontets[i/5].tet = nullptr;
      }
    }
  }

  // Collect all exterior tets (in concave place and in holes).
  for (i = 0; i < tetarray->objects; i++) {
    parytet = (triface *) fastlookup(tetarray, i);
    j = (parytet->ver & 3); // j is the current face number.
    // Check the other three adjacent tets.
    for (k = 1; k < 4; k++) {
      decode(parytet->tet[(j + k) % 4], neightet); 
      // neightet may be a hull tet.
      if (!infected(neightet)) {
        // Is neightet protected by a subface.
        if (!issubface(neightet)) {
          // Not proected. Collect it. (It must not be a hull tet).
          infect(neightet);
          tetarray->newindex((void **) &parytet1);
          *parytet1 = neightet;
        } else {
          // Protected. Check if it is a hull tet.
          if (ishulltet(neightet)) {
            // A hull tet. Collect it.
            infect(neightet);
            hullarray->newindex((void **) &parytet1);
            *parytet1 = neightet;
            // Both sides of this subface are exterior.
            tspivot(neightet, checksh);
            // Queue this subface (to be deleted later).
            assert(!sinfected(checksh));
            sinfect(checksh); // Only queue it once.
            subfacstack->newindex((void **) &parysh);
            *parysh = checksh;
          }
        }
      } else {
        // Both sides of this face are in exterior.
        // If there is a subface. It should be collected.
        if (issubface(neightet)) {
          tspivot(neightet, checksh);
          if (!sinfected(checksh)) {
            sinfect(checksh);
            subfacstack->newindex((void **) &parysh);
            *parysh = checksh;
          }
        }
      }
    } // j, k
  } // i

  if (b->regionattrib && (in->numberofregions > 0)) {
    // Re-check saved region tets to see if they lie outside.
    for (i = 0; i < in->numberofregions; i++) {
      if (infected(regiontets[i])) {
        if (b->verbose) {
          printf("Warning:  The %d-th region point ", i+1);
          printf("lies in the exterior of the domain.\n");
        }
        regiontets[i].tet = nullptr;
      }
    }
  }

  // Collect vertices which point to infected tets. These vertices
  //   may get deleted after the removal of exterier tets.
  //   If -Y1 option is used, collect all Steiner points for removal.
  //   The lists 'cavetetvertlist' and 'subvertstack' are re-used.
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != nullptr) {
    if ((pointtype(ptloop) != UNUSEDVERTEX) &&
        (pointtype(ptloop) != DUPLICATEDVERTEX)) {
      decode(point2tet(ptloop), neightet);
      if (infected(neightet)) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = ptloop;
      }
      if (b->nobisect && (b->nobisect_param > 0)) { // -Y1
        // Queue it if it is a Steiner point.
        if (pointmark(ptloop) > 
              (in->numberofpoints - (in->firstnumber ? 0 : 1))) {
          subvertstack->newindex((void **) &parypt);
          *parypt = ptloop;
        }
      }
    }
    ptloop = pointtraverse();
  }

  if (!b->convex && (tetarray->objects > 0l)) { // No -c option.
    // Remove exterior tets. Hull tets are updated.
    arraypool *newhullfacearray;
    triface hulltet, casface;
    point pa, pb, pc;

    newhullfacearray = new arraypool(sizeof(triface), 10);

    // Create and save new hull tets.
    for (i = 0; i < tetarray->objects; i++) {
      parytet = (triface *) fastlookup(tetarray, i);
      for (j = 0; j < 4; j++) {
        decode(parytet->tet[j], tetloop);
        if (!infected(tetloop)) {
          // Found a new hull face (must be a subface).
          tspivot(tetloop, checksh);
          maketetrahedron(&hulltet);
          pa = org(tetloop);
          pb = dest(tetloop);
          pc = apex(tetloop);
          setvertices(hulltet, pb, pa, pc, dummypoint);
          bond(tetloop, hulltet);
          // Update the subface-to-tet map.
          sesymself(checksh);
          tsbond(hulltet, checksh);
          // Update the segment-to-tet map.
          for (k = 0; k < 3; k++) {
            if (issubseg(tetloop)) {
              tsspivot1(tetloop, checkseg);
              tssbond1(hulltet, checkseg);
              sstbond1(checkseg, hulltet);
            }
            enextself(tetloop);
            eprevself(hulltet);
          }
          // Update the point-to-tet map.
          setpoint2tet(pa, (tetrahedron) tetloop.tet);
          setpoint2tet(pb, (tetrahedron) tetloop.tet);
          setpoint2tet(pc, (tetrahedron) tetloop.tet);
          // Save the exterior tet at this hull face. It still holds pointer
          //   to the adjacent interior tet. Use it to connect new hull tets. 
          newhullfacearray->newindex((void **) &parytet1);
          parytet1->tet = parytet->tet;
          parytet1->ver = j;
        } // if (!infected(tetloop))
      } // j
    } // i

    // Connect new hull tets.
    for (i = 0; i < newhullfacearray->objects; i++) {
      parytet = (triface *) fastlookup(newhullfacearray, i);
      fsym(*parytet, neightet);
      // Get the new hull tet.
      fsym(neightet, hulltet);
      for (j = 0; j < 3; j++) {
        esym(hulltet, casface);
        if (casface.tet[casface.ver & 3] == nullptr) {
          // Since the boundary of the domain may not be a manifold, we
          //   find the adjacent hull face by travesing the tets in the
          //   exterior (which are all infected tets).
          neightet = *parytet;
          while (1) {
            fnextself(neightet);
            if (!infected(neightet)) break;
          }
          if (!ishulltet(neightet)) {
            // An interior tet. Get the new hull tet.
            fsymself(neightet);
            esymself(neightet);
          } 
          // Bond them together.
          bond(casface, neightet);
        }
        enextself(hulltet);
        enextself(*parytet);
      } // j
    } // i

    if (subfacstack->objects > 0l) {
      // Remove all subfaces which do not attach to any tetrahedron.
      //   Segments which are not attached to any subfaces and tets
      //   are deleted too.
      face casingout, casingin;
      long delsegcount = 0l;

      for (i = 0; i < subfacstack->objects; i++) {
        parysh = (face *) fastlookup(subfacstack, i);
        if (i == 0) {
          if (b->verbose) {
            printf("Warning:  Removing an open face (%d, %d, %d)\n",
                   pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
                   pointmark(sapex(*parysh)));
          }
        }
        // Dissolve this subface from face links.
        for (j = 0; j < 3; j++) {         
          spivot(*parysh, casingout);
          sspivot(*parysh, checkseg);
          if (casingout.sh != nullptr) {
            casingin = casingout;
            while (1) {
              spivot(casingin, checksh);
              if (checksh.sh == parysh->sh) break;
              casingin = checksh;
            }
            if (casingin.sh != casingout.sh) {
              // Update the link: ... -> casingin -> casingout ->...
              sbond1(casingin, casingout);
            } else {
              // Only one subface at this edge is left.
              sdissolve(casingout);
            }
            if (checkseg.sh != nullptr) {
              // Make sure the segment does not connect to a dead one.
              ssbond(casingout, checkseg);
            }
          } else {
            if (checkseg.sh != nullptr) {
              // The segment is also dead.
              if (delsegcount == 0) {
                if (b->verbose) {
                  printf("Warning:  Removing a dangling segment (%d, %d)\n",
                       pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
                }
              }
              shellfacedealloc(subsegs, checkseg.sh);
              delsegcount++;
            }
          }
          senextself(*parysh);
        } // j
        // Delete this subface.
        shellfacedealloc(subfaces, parysh->sh);
      } // i
      if (b->verbose) {
        printf("  Deleted %ld subfaces.\n", subfacstack->objects);
        if (delsegcount > 0) {
          printf("  Deleted %ld segments.\n", delsegcount);
        }
      }
      subfacstack->restart();
    } // if (subfacstack->objects > 0l)

    if (cavetetvertlist->objects > 0l) {
      // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX.
      long delvertcount = unuverts;
      long delsteinercount = 0l;

      for (i = 0; i < cavetetvertlist->objects; i++) {
        parypt = (point *) fastlookup(cavetetvertlist, i);
        decode(point2tet(*parypt), neightet);
        if (infected(neightet)) {
          // Found an exterior vertex.
          if (pointmark(*parypt) > 
                (in->numberofpoints - (in->firstnumber ? 0 : 1))) {
            // A Steiner point.
            if (pointtype(*parypt) == FREESEGVERTEX) {
              st_segref_count--;
            } else if (pointtype(*parypt) == FREEFACETVERTEX) {
              st_facref_count--;
            } else {
              assert(pointtype(*parypt) == FREEVOLVERTEX);
              st_volref_count--;
            }
            delsteinercount++;
            if (steinerleft > 0) steinerleft++;
          }
          setpointtype(*parypt, UNUSEDVERTEX);
          unuverts++;
        }
      }

      if (b->verbose) {
        if (unuverts > delvertcount) {
          if (delsteinercount > 0l) {
            if (unuverts > (delvertcount + delsteinercount)) {
              printf("  Removed %ld exterior input vertices.\n", 
                     unuverts - delvertcount - delsteinercount);
            }
            printf("  Removed %ld exterior Steiner vertices.\n", 
                   delsteinercount);
          } else {
            printf("  Removed %ld exterior input vertices.\n", 
                   unuverts - delvertcount);
          }
        }
      }
      cavetetvertlist->restart();
      // Comment: 'subvertstack' will be cleaned in routine
      //   suppresssteinerpoints().
    } // if (cavetetvertlist->objects > 0l)

    // Update the hull size.
    hullsize += (newhullfacearray->objects - hullarray->objects);

    // Delete all exterior tets and old hull tets.
    for (i = 0; i < tetarray->objects; i++) {
      parytet = (triface *) fastlookup(tetarray, i);
      tetrahedrondealloc(parytet->tet);
    }
    tetarray->restart();

    for (i = 0; i < hullarray->objects; i++) {
      parytet = (triface *) fastlookup(hullarray, i);
      tetrahedrondealloc(parytet->tet);
    }
    hullarray->restart();

    delete newhullfacearray;
  } // if (!b->convex && (tetarray->objects > 0l))

  if (b->convex && (tetarray->objects > 0l)) { // With -c option
    // In this case, all exterior tets get a region marker '-1'.
    assert(b->regionattrib > 0); // -A option must be enabled.
    int attrnum = numelemattrib - 1;

    for (i = 0; i < tetarray->objects; i++) {
      parytet = (triface *) fastlookup(tetarray, i);
      setelemattribute(parytet->tet, attrnum, -1);
    }
    tetarray->restart();

    for (i = 0; i < hullarray->objects; i++) {
      parytet = (triface *) fastlookup(hullarray, i);
      uninfect(*parytet);
    }
    hullarray->restart();

    if (subfacstack->objects > 0l) {
      for (i = 0; i < subfacstack->objects; i++) {
        parysh = (face *) fastlookup(subfacstack, i);
        suninfect(*parysh);
      }
      subfacstack->restart();
    }

    if (cavetetvertlist->objects > 0l) {
      cavetetvertlist->restart();
    }
  } // if (b->convex && (tetarray->objects > 0l))

  if (b->regionattrib) { // With -A option.
    if (!b->quiet) {
      printf("Spreading region attributes.\n");
    }
    REAL volume;
    int attr, maxattr = 0; // Choose a small number here.
    int attrnum = numelemattrib - 1; 
    // Comment: The element region marker is at the end of the list of
    //   the element attributes.
    int regioncount = 0;

    // If has user-defined region attributes.
    if (in->numberofregions > 0) {
      // Spread region attributes.
      for (i = 0; i < 5 * in->numberofregions; i += 5) {
        if (regiontets[i/5].tet != nullptr) {
          attr = (int) in->regionlist[i + 3];
          if (attr > maxattr) {
            maxattr = attr;
          }
          volume = in->regionlist[i + 4];
          tetarray->restart(); // Re-use this array.
          infect(regiontets[i/5]);
          tetarray->newindex((void **) &parytet);
          *parytet = regiontets[i/5];
          // Collect and set attrs for all tets of this region.
          for (j = 0; j < tetarray->objects; j++) {
            parytet = (triface *) fastlookup(tetarray, j);
            tetloop = *parytet;
            setelemattribute(tetloop.tet, attrnum, attr);
            if (b->varvolume) { // If has -a option.
              setvolumebound(tetloop.tet, volume);
            }
            for (k = 0; k < 4; k++) {
              decode(tetloop.tet[k], neightet);
              // Is the adjacent already checked?
              if (!infected(neightet)) {
                // Is this side protected by a subface?
                if (!issubface(neightet)) {
                  infect(neightet);
                  tetarray->newindex((void **) &parytet);
                  *parytet = neightet;
                }
              }
            } // k
          } // j
          regioncount++;
        } // if (regiontets[i/5].tet != nullptr)
      } // i
    }

    // Set attributes for all tetrahedra.
    attr = maxattr + 1;
    tetrahedrons->traversalinit();
    tetloop.tet = tetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) nullptr) {
      if (!infected(tetloop)) {
        // An unmarked region.
        tetarray->restart(); // Re-use this array.
        infect(tetloop);
        tetarray->newindex((void **) &parytet);
        *parytet = tetloop;
        // Find and mark all tets.
        for (j = 0; j < tetarray->objects; j++) {
          parytet = (triface *) fastlookup(tetarray, j);
          tetloop = *parytet;
          setelemattribute(tetloop.tet, attrnum, attr);
          for (k = 0; k < 4; k++) {
            decode(tetloop.tet[k], neightet);
            // Is the adjacent tet already checked?
            if (!infected(neightet)) {
              // Is this side protected by a subface?
              if (!issubface(neightet)) {
                infect(neightet);
                tetarray->newindex((void **) &parytet);
                *parytet = neightet;
              }
            }
          } // k
        } // j
        attr++; // Increase the attribute.
        regioncount++;
      }
      tetloop.tet = tetrahedrontraverse();
    }
    // Until here, every tet has a region attribute.

    // Uninfect processed tets.
    tetrahedrons->traversalinit();
    tetloop.tet = tetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) nullptr) {
      uninfect(tetloop);
      tetloop.tet = tetrahedrontraverse();
    }

    if (b->verbose) {
      //assert(regioncount > 0);
      if (regioncount > 1) {
        printf("  Found %d subdomains.\n", regioncount);
      } else {
        printf("  Found %d domain.\n", regioncount);
      }
    }
  } // if (b->regionattrib)

  if (regiontets != nullptr) {
    delete [] regiontets;
  }
  delete tetarray;
  delete hullarray;

  if (!b->convex) { // No -c option
    // The mesh is non-convex now.
    nonconvex = 1;

    // Push all hull tets into 'flipstack'.
    tetrahedrons->traversalinit();
    tetloop.ver = 11; // The face opposite to dummypoint.
    tetloop.tet = alltetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) nullptr) {
      if ((point) tetloop.tet[7] == dummypoint) {
        fsym(tetloop, neightet);
        flippush(flipstack, &neightet);
      }
      tetloop.tet = alltetrahedrontraverse();
    }

    flipconstraints fc;
    fc.enqflag = 2;
    long sliver_peel_count = lawsonflip3d(&fc);

    if (sliver_peel_count > 0l) {
      if (b->verbose) {
        printf("  Removed %ld hull slivers.\n", sliver_peel_count);
      }
    }
    unflipqueue->restart();
  } // if (!b->convex)
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// reconstructmesh()    Reconstruct a tetrahedral mesh.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::reconstructmesh()
{
  tetrahedron *ver2tetarray;
  point *idx2verlist;
  triface tetloop, checktet, prevchktet;
  triface hulltet, face1, face2;
  tetrahedron tptr;
  face subloop, neighsh, nextsh;
  face segloop;
  shellface sptr;
  point p[4], q[3];
  REAL ori, attrib, volume;
  REAL angtol, ang;
  int eextras, marker = 0;
  int bondflag;
  int t1ver;
  int idx, i, j, k;

  if (!b->quiet) {
    printf("Reconstructing mesh ...\n");
  }

  if (b->convex) { // -c option.
    // Assume the mesh is convex. Exterior tets have region attribute -1.
    assert(in->numberoftetrahedronattributes > 0);
  } else {
    // Assume the mesh is non-convex.
    nonconvex = 1;
  }

  // Create a map from indices to vertices. 
  makeindex2pointmap(idx2verlist);
  // 'idx2verlist' has length 'in->numberofpoints + 1'.
  if (in->firstnumber == 1) {
    idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint.
  }

  // Allocate an array that maps each vertex to its adjacent tets.
  ver2tetarray = new tetrahedron[in->numberofpoints + 1];
  //for (i = 0; i < in->numberofpoints + 1; i++) {
  for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) {
    setpointtype(idx2verlist[i], VOLVERTEX); // initial type.
    ver2tetarray[i] = nullptr;
  }

  // Create the tetrahedra and connect those that share a common face.
  for (i = 0; i < in->numberoftetrahedra; i++) {
    // Get the four vertices.
    idx = i * in->numberofcorners;
    for (j = 0; j < 4; j++) {
      p[j] = idx2verlist[in->tetrahedronlist[idx++]];
    }
    // Check the orientation.
    ori = orient3d(p[0], p[1], p[2], p[3]);
    if (ori > 0.0) {
      // Swap the first two vertices.
      q[0] = p[0]; p[0] = p[1]; p[1] = q[0];
    } else if (ori == 0.0) {
      if (!b->quiet) {
        printf("Warning:  Tet #%d is degenerate.\n", i + in->firstnumber);
      }
    }
    // Create a new tetrahedron.
    maketetrahedron(&tetloop); // tetloop.ver = 11.
    setvertices(tetloop, p[0], p[1], p[2], p[3]);
    // Set element attributes if they exist.
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      idx = i * in->numberoftetrahedronattributes;
      attrib = in->tetrahedronattributelist[idx + j];
      setelemattribute(tetloop.tet, j, attrib);
    }
    // If -a switch is used (with no number follows) Set a volume
    //   constraint if it exists.
    if (b->varvolume) {
      if (in->tetrahedronvolumelist != (REAL *) nullptr) {
        volume = in->tetrahedronvolumelist[i];
      } else {
        volume = -1.0;
      }
      setvolumebound(tetloop.tet, volume);
    }
    // Try connecting this tet to others that share the common faces.
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      p[3] = oppo(tetloop);
      // Look for other tets having this vertex.
      idx = pointmark(p[3]);
      tptr = ver2tetarray[idx];
      // Link the current tet to the next one in the stack.
      tetloop.tet[8 + tetloop.ver] = tptr;
      // Push the current tet onto the stack.
      ver2tetarray[idx] = encode(tetloop);
      decode(tptr, checktet);
      if (checktet.tet != nullptr) {
        p[0] =  org(tetloop); // a
        p[1] = dest(tetloop); // b
        p[2] = apex(tetloop); // c
        prevchktet = tetloop;
        do {
          q[0] =  org(checktet); // a'
          q[1] = dest(checktet); // b'
          q[2] = apex(checktet); // c'
          // Check the three faces at 'd' in 'checktet'.
          bondflag = 0;
          for (j = 0; j < 3; j++) {
            // Go to the face [b',a',d], or [c',b',d], or [a',c',d].
            esym(checktet, face2);
            if (face2.tet[face2.ver & 3] == nullptr) {
              k = ((j + 1) % 3);
              if (q[k] == p[0]) {   // b', c', a' = a
                if (q[j] == p[1]) { // a', b', c' = b
                  // [#,#,d] is matched to [b,a,d].
                  esym(tetloop, face1);
                  bond(face1, face2);
                  bondflag++;
                }
              }
              if (q[k] == p[1]) {   // b',c',a' = b
                if (q[j] == p[2]) { // a',b',c' = c
                  // [#,#,d] is matched to [c,b,d].
                  enext(tetloop, face1);
                  esymself(face1);
                  bond(face1, face2);
                  bondflag++;
                }
              }
              if (q[k] == p[2]) {   // b',c',a' = c
                if (q[j] == p[0]) { // a',b',c' = a
                  // [#,#,d] is matched to [a,c,d].
                  eprev(tetloop, face1);
                  esymself(face1);
                  bond(face1, face2);
                  bondflag++;
                }
              }
            } else {
              bondflag++;
            }
            enextself(checktet);
          } // j
          // Go to the next tet in the link.
          tptr = checktet.tet[8 + checktet.ver];
          if (bondflag == 3) {
            // All three faces at d in 'checktet' have been connected.
            // It can be removed from the link.            
            prevchktet.tet[8 + prevchktet.ver] = tptr;
          } else {
            // Bakup the previous tet in the link.
            prevchktet = checktet;
          }
          decode(tptr, checktet);
        } while (checktet.tet != nullptr);
      } // if (checktet.tet != nullptr)
    } // for (tetloop.ver = 0; ...
  } // i

  // Remember a tet of the mesh.
  recenttet = tetloop;

  // Create hull tets, create the point-to-tet map, and clean up the
  //   temporary spaces used in each tet. 
  hullsize = tetrahedrons->items;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    tptr = encode(tetloop);
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      if (tetloop.tet[tetloop.ver] == nullptr) {
        // Create a hull tet.
        maketetrahedron(&hulltet);
        p[0] =  org(tetloop);
        p[1] = dest(tetloop);
        p[2] = apex(tetloop);
        setvertices(hulltet, p[1], p[0], p[2], dummypoint);
        bond(tetloop, hulltet);
        // Try connecting this to others that share common hull edges.
        for (j = 0; j < 3; j++) {
          fsym(hulltet, face2);
          while (1) {
            if (face2.tet == nullptr) break;
            esymself(face2);
            if (apex(face2) == dummypoint) break;
            fsymself(face2);
          }
          if (face2.tet != nullptr) {
            // Found an adjacent hull tet.
            assert(face2.tet[face2.ver & 3] == nullptr);
            esym(hulltet, face1);
            bond(face1, face2);
          }
          enextself(hulltet);
        }
        //hullsize++;
      }
      // Create the point-to-tet map.
      setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr);
      // Clean the temporary used space.
      tetloop.tet[8 + tetloop.ver] = nullptr;
    }
    tetloop.tet = tetrahedrontraverse();
  }

  hullsize = tetrahedrons->items - hullsize;

  // Subfaces will be inserted into the mesh. 
  if (in->trifacelist != nullptr) {
    // A .face file is given. It may contain boundary faces. Insert them.
    for (i = 0; i < in->numberoftrifaces; i++) {
      // Is it a subface?
      if (in->trifacemarkerlist != nullptr) {
        marker = in->trifacemarkerlist[i];
      } else {
        // Face markers are not available. Assume all of them are subfaces.
        marker = 1;
      }
      if (marker > 0) {
        idx = i * 3;
        for (j = 0; j < 3; j++) {
          p[j] = idx2verlist[in->trifacelist[idx++]];
        }
        // Search the subface.
        bondflag = 0;
        // Make sure all vertices are in the mesh. Avoid crash.
        for (j = 0; j < 3; j++) {
          decode(point2tet(p[j]), checktet);
          if (checktet.tet == nullptr) break;
        }
        if ((j == 3) && getedge(p[0], p[1], &checktet)) {
          tetloop = checktet;
          q[2] = apex(checktet);
          while (1) {
            if (apex(tetloop) == p[2]) {
              // Found the face.
              // Check if there exist a subface already?
              tspivot(tetloop, neighsh); 
              if (neighsh.sh != nullptr) {
                // Found a duplicated subface. 
                // This happens when the mesh was generated by other mesher.
                bondflag = 0;
              } else {
                bondflag = 1;
              }
              break;
            }
            fnextself(tetloop);
            if (apex(tetloop) == q[2]) break;
          }
        }
        if (bondflag) {
          // Create a new subface.
          makeshellface(subfaces, &subloop);
          setshvertices(subloop, p[0], p[1], p[2]);
          // Create the point-to-subface map.
          sptr = sencode(subloop);
          for (j = 0; j < 3; j++) {
            setpointtype(p[j], FACETVERTEX); // initial type.
            setpoint2sh(p[j], sptr);
          }
          if (in->trifacemarkerlist != nullptr) {
            setshellmark(subloop, in->trifacemarkerlist[i]);
          }
          // Insert the subface into the mesh.
          tsbond(tetloop, subloop);
          fsymself(tetloop);
          sesymself(subloop);
          tsbond(tetloop, subloop);
        } else {
          if (!b->quiet) {
            if (neighsh.sh == nullptr) {
              printf("Warning:  Subface #%d [%d,%d,%d] is missing.\n", 
                     i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
                     pointmark(p[2]));
            } else {
              printf("Warning: Ignore a dunplicated subface #%d [%d,%d,%d].\n", 
                     i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
                     pointmark(p[2]));
            }
          }
        } // if (bondflag)
      } // if (marker > 0)
    } // i
  } // if (in->trifacelist)

  if (!b->convex) { // Without -c option.
    // Indentify subfaces from the mesh.
    // Create subfaces for hull faces (if they're not subface yet) and
    //   interior faces which separate two different materials.
    eextras = in->numberoftetrahedronattributes;
    tetrahedrons->traversalinit();
    tetloop.tet = tetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) nullptr) {
      for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
        tspivot(tetloop, neighsh);
        if (neighsh.sh == nullptr) {
          bondflag = 0;
          fsym(tetloop, checktet);
          if (ishulltet(checktet)) {
            bondflag = 1;  // A hull face.
          } else {
            if (eextras > 0) {
              if (elemattribute(tetloop.tet, eextras - 1) !=
                  elemattribute(checktet.tet, eextras - 1)) {
                bondflag = 1; // An interior interface.
              }
            }
          }
          if (bondflag) {
            // Create a new subface.
            makeshellface(subfaces, &subloop);
            p[0] = org(tetloop);
            p[1] = dest(tetloop);
            p[2] = apex(tetloop);
            setshvertices(subloop, p[0], p[1], p[2]);
            // Create the point-to-subface map.
            sptr = sencode(subloop);
            for (j = 0; j < 3; j++) {
              setpointtype(p[j], FACETVERTEX); // initial type.
              setpoint2sh(p[j], sptr);
            }
            setshellmark(subloop, 0); // Default marker.
            // Insert the subface into the mesh.
            tsbond(tetloop, subloop);
            sesymself(subloop);
            tsbond(checktet, subloop);
          } // if (bondflag)
        } // if (neighsh.sh == nullptr)
      }
      tetloop.tet = tetrahedrontraverse();
    }
  } // if (!b->convex)

  // Connect subfaces together. 
  subfaces->traversalinit();
  subloop.shver = 0;
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) nullptr) {
    for (i = 0; i < 3; i++) {
      spivot(subloop, neighsh);
      if (neighsh.sh == nullptr) {
        // Form a subface ring by linking all subfaces at this edge.
        // Traversing all faces of the tets at this edge.
        stpivot(subloop, tetloop);
        q[2] = apex(tetloop);
        neighsh = subloop;
        while (1) {
          fnextself(tetloop);
          tspivot(tetloop, nextsh);
          if (nextsh.sh != nullptr) {
            // Link neighsh <= nextsh.
            sbond1(neighsh, nextsh);
            neighsh = nextsh;
          }
          if (apex(tetloop) == q[2]) {
            assert(nextsh.sh == subloop.sh); // It's a ring.
            break;
          }
        } // while (1)
      } // if (neighsh.sh == nullptr)
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }


  // Segments will be introudced. 
  if (in->edgelist != nullptr) {
    // A .edge file is given. It may contain boundary edges. Insert them.
    for (i = 0; i < in->numberofedges; i++) {
      // Is it a segment?
      if (in->edgemarkerlist != nullptr) {
        marker = in->edgemarkerlist[i];
      } else {
        // Edge markers are not available. Assume all of them are segments.
        marker = 1;
      }
      if (marker != 0) { 
        // Insert a segment.
        idx = i * 2;
        for (j = 0; j < 2; j++) {
          p[j] = idx2verlist[in->edgelist[idx++]];
        }
        // Make sure all vertices are in the mesh. Avoid crash.
        for (j = 0; j < 2; j++) {
          decode(point2tet(p[j]), checktet);
          if (checktet.tet == nullptr) break;
        }
        // Search the segment.
        if ((j == 2) && getedge(p[0], p[1], &checktet)) {
          // Create a new subface.
          makeshellface(subsegs, &segloop);
          setshvertices(segloop, p[0], p[1], nullptr);
          // Create the point-to-segment map.
          sptr = sencode(segloop);
          for (j = 0; j < 2; j++) {
            setpointtype(p[j], RIDGEVERTEX); // initial type.
            setpoint2sh(p[j], sptr);
          }
          if (in->edgemarkerlist != nullptr) {
            setshellmark(segloop, marker);
          }
          // Insert the segment into the mesh.
          tetloop = checktet;
          q[2] = apex(checktet);
          subloop.sh = nullptr;
          while (1) {
            tssbond1(tetloop, segloop);
            tspivot(tetloop, subloop);
            if (subloop.sh != nullptr) {
              ssbond1(subloop, segloop);
              sbond1(segloop, subloop);
            }
            fnextself(tetloop);
            if (apex(tetloop) == q[2]) break;
          } // while (1)
          // Remember an adjacent tet for this segment.
          sstbond1(segloop, tetloop);
        } else {
          if (!b->quiet) {
            printf("Warning:  Segment #%d [%d,%d] is missing.\n", 
                   i + in->firstnumber, pointmark(p[0]), pointmark(p[1]));
          }
        }
      } // if (marker != 0)
    } // i
  } // if (in->edgelist)

  if (!b->convex) { // Without -c option.
    // Identify segments from the mesh. 
    // Create segments for non-manifold edges (which are shared by more 
    //   than two subfaces), and for non-coplanar edges, i.e., two subfaces
    //   form an dihedral angle > 'b->facet_ang_tol' (degree).
    angtol = b->facet_ang_tol / 180.0 * PI;
    subfaces->traversalinit();
    subloop.shver = 0;
    subloop.sh = shellfacetraverse(subfaces);
    while (subloop.sh != (shellface *) nullptr) {
      for (i = 0; i < 3; i++) {
        sspivot(subloop, segloop);
        if (segloop.sh == nullptr) {
          // Check if this edge is a segment.
          bondflag = 0;
          // Counter the number of subfaces at this edge.
          idx = 0;
          nextsh = subloop;
          while (1) {
            idx++;
            spivotself(nextsh);
            if (nextsh.sh == subloop.sh) break;
          }
          if (idx != 2) {
            // It's a non-manifold edge. Insert a segment.
            p[0] = sorg(subloop);
            p[1] = sdest(subloop);
            bondflag = 1;
          } else {
            // Check the dihedral angle formed by the two subfaces.
            spivot(subloop, neighsh);
            p[0] = sorg(subloop);
            p[1] = sdest(subloop);
            p[2] = sapex(subloop);
            p[3] = sapex(neighsh);
            ang = facedihedral(p[0], p[1], p[2], p[3]);
            if (ang > PI) ang = 2 * PI - ang;
            if (ang < angtol) {
              bondflag = 1;
            }
          }
          if (bondflag) {
            // Create a new subface.
            makeshellface(subsegs, &segloop);
            setshvertices(segloop, p[0], p[1], nullptr);
            // Create the point-to-segment map.
            sptr = sencode(segloop);
            for (j = 0; j < 2; j++) {
              setpointtype(p[j], RIDGEVERTEX); // initial type.
              setpoint2sh(p[j], sptr);
            }
            setshellmark(segloop, marker);
            // Insert the subface into the mesh.
            stpivot(subloop, tetloop);
            q[2] = apex(tetloop);
            while (1) {
              tssbond1(tetloop, segloop);
              tspivot(tetloop, neighsh);
              if (neighsh.sh != nullptr) {
                ssbond1(neighsh, segloop);
              }
              fnextself(tetloop);
              if (apex(tetloop) == q[2]) break;
            } // while (1)
            // Remember an adjacent tet for this segment.
            sstbond1(segloop, tetloop);
            sbond1(segloop, subloop);
          } // if (bondflag)
        } // if (neighsh.sh == nullptr)
        senextself(subloop);
      }
      subloop.sh = shellfacetraverse(subfaces);
    }
  } // if (!b->convex)

  // Remember the number of input segments.
  insegments = subsegs->items;


  // Set global flags.
  checksubsegflag = 1;
  checksubfaceflag = 1;

  delete [] idx2verlist;
  delete [] ver2tetarray;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutpoint()    Search a point in mesh.                                   //
//                                                                           //
// This function searches the point in a mesh whose domain may be not convex.//
// In case of a convex domain, the locate() function is sufficient.          //
//                                                                           //
// If 'randflag' is used, randomly select a start searching tet.  Otherwise, //
// start searching directly from 'searchtet'.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag)
{
  point pa, pb, pc, pd;
  enum locateresult loc = OUTSIDE;
  REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0;
  int t1ver;


  // Randonmly select a good starting tet.
  if (randflag) {
    randomsample(searchpt, searchtet);
  } else {
    if (searchtet->tet == nullptr) {
      *searchtet = recenttet;
    }
  }
  loc = locate(searchpt, searchtet, 0);

  if (loc == OUTSIDE) {
    // Test if it lies nearly on the hull face.
    // Reuse vol, ori1.
    pa = org(*searchtet);
    pb = dest(*searchtet);
    pc = apex(*searchtet);
    vol = triarea(pa, pb, pc);
    ori1 = orient3dfast(pa, pb, pc, searchpt);
    if (fabs(ori1 / vol) < b->epsilon) {
      loc = ONFACE; // On face (or on edge, or on vertex).
      fsymself(*searchtet);
    }
  }

  if (loc != OUTSIDE) {
    // Round the result of location.
    pa = org(*searchtet);
    pb = dest(*searchtet);
    pc = apex(*searchtet);
    pd = oppo(*searchtet);
    vol = orient3dfast(pa, pb, pc, pd);
    ori1 = orient3dfast(pa, pb, pc, searchpt);
    ori2 = orient3dfast(pb, pa, pd, searchpt);
    ori3 = orient3dfast(pc, pb, pd, searchpt);
    ori4 = orient3dfast(pa, pc, pd, searchpt);
    if (fabs(ori1 / vol) < b->epsilon) ori1 = 0;
    if (fabs(ori2 / vol) < b->epsilon) ori2 = 0;
    if (fabs(ori3 / vol) < b->epsilon) ori3 = 0;
    if (fabs(ori4 / vol) < b->epsilon) ori4 = 0;
  } else { // if (loc == OUTSIDE) {
    // Do a brute force search for the point (with rounding).
    tetrahedrons->traversalinit();
    searchtet->tet = tetrahedrontraverse();
    while (searchtet->tet != nullptr) {
      pa = org(*searchtet);
      pb = dest(*searchtet);
      pc = apex(*searchtet);
      pd = oppo(*searchtet);

      vol = orient3dfast(pa, pb, pc, pd); 
      if (vol < 0) {
        ori1 = orient3dfast(pa, pb, pc, searchpt);
        if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding.
        if (ori1 <= 0) {
          ori2 = orient3dfast(pb, pa, pd, searchpt);
          if (fabs(ori2 / vol) < b->epsilon) ori2 = 0;
          if (ori2 <= 0) {
            ori3 = orient3dfast(pc, pb, pd, searchpt);
            if (fabs(ori3 / vol) < b->epsilon) ori3 = 0;
            if (ori3 <= 0) {
              ori4 = orient3dfast(pa, pc, pd, searchpt);
              if (fabs(ori4 / vol) < b->epsilon) ori4 = 0;
              if (ori4 <= 0) {
                // Found the tet. Return its location. 
                break;
              } // ori4
            } // ori3
          } // ori2
        } // ori1
      }

      searchtet->tet = tetrahedrontraverse();
    } // while (searchtet->tet != nullptr)
    nonregularcount++;  // Re-use this counter.
  }

  if (searchtet->tet != nullptr) {
    // Return the point location.
    if (ori1 == 0) { // on face [a,b,c]
      if (ori2 == 0) { // on edge [a,b].
        if (ori3 == 0) { // on vertex [b].
          assert(ori4 != 0);
          enextself(*searchtet); // [b,c,a,d]
          loc = ONVERTEX;
        } else {
          if (ori4 == 0) { // on vertex [a]
            loc =  ONVERTEX; // [a,b,c,d]
          } else {    
            loc =  ONEDGE; // [a,b,c,d]
          }
        }
      } else { // ori2 != 0
        if (ori3 == 0) { // on edge [b,c]
          if (ori4 == 0) { // on vertex [c]
            eprevself(*searchtet); // [c,a,b,d]
            loc =  ONVERTEX;
          } else {
            enextself(*searchtet); // [b,c,a,d]
            loc =  ONEDGE;
          }
        } else { // ori3 != 0
          if (ori4 == 0) { // on edge [c,a]
            eprevself(*searchtet); // [c,a,b,d]
            loc =  ONEDGE;
          } else {
            loc =  ONFACE;
          }
        }
      }
    } else { // ori1 != 0
      if (ori2 == 0) { // on face [b,a,d]
        esymself(*searchtet); // [b,a,d,c]
        if (ori3 == 0) { // on edge [b,d]
          eprevself(*searchtet); // [d,b,a,c]
          if (ori4 == 0) { // on vertex [d]                      
            loc =  ONVERTEX;
          } else {
            loc =  ONEDGE;
          }
        } else { // ori3 != 0
          if (ori4 == 0) { // on edge [a,d]
            enextself(*searchtet); // [a,d,b,c]
            loc =  ONEDGE;
          } else {
            loc =  ONFACE;
          }
        }
      } else { // ori2 != 0
        if (ori3 == 0) { // on face [c,b,d]
          enextself(*searchtet);
          esymself(*searchtet);
          if (ori4 == 0) { // on edge [c,d]
            eprevself(*searchtet);
            loc =  ONEDGE;
          } else {
            loc =  ONFACE;
          }
        } else {
          if (ori4 == 0) { // on face [a,c,d]
            eprevself(*searchtet);
            esymself(*searchtet);
            loc =  ONFACE;
          } else { // inside tet [a,b,c,d]
            loc =  INTETRAHEDRON;
          } // ori4
        } // ori3
      } // ori2
    } // ori1
  } else {
    loc = OUTSIDE;
  }

  return (int) loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getpointmeshsize()    Interpolate the mesh size at given point.           //
//                                                                           //
// 'iloc' indicates the location of the point w.r.t. 'searchtet'.  The size  //
// is obtained by linear interpolation on the vertices of the tet.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc)
{
  point *pts, pa, pb, pc;
  REAL volume, vol[4], wei[4];
  REAL size;
  int i;

  size = 0;

  if (iloc == (int) INTETRAHEDRON) {
    pts = (point *) &(searchtet->tet[4]);
    assert(pts[3] != dummypoint);
    // Only do interpolation if all vertices have non-zero sizes.
    if ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) &&
        (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0)) {
      // P1 interpolation.
      volume = orient3dfast(pts[0], pts[1], pts[2], pts[3]);
      vol[0] = orient3dfast(searchpt, pts[1], pts[2], pts[3]);
      vol[1] = orient3dfast(pts[0], searchpt, pts[2], pts[3]);
      vol[2] = orient3dfast(pts[0], pts[1], searchpt, pts[3]);
      vol[3] = orient3dfast(pts[0], pts[1], pts[2], searchpt);
      for (i = 0; i < 4; i++) {
        wei[i] = fabs(vol[i] / volume);
        size += (wei[i] * pts[i][pointmtrindex]);
      }
    }
  } else if (iloc == (int) ONFACE) {
    pa = org(*searchtet);
    pb = dest(*searchtet);
    pc = apex(*searchtet);
    if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) &&
        (pc[pointmtrindex] > 0)) {
      volume = triarea(pa, pb, pc);
      vol[0] = triarea(searchpt, pb, pc);
      vol[1] = triarea(pa, searchpt, pc);
      vol[2] = triarea(pa, pb, searchpt);
      size = (vol[0] / volume) * pa[pointmtrindex]
           + (vol[1] / volume) * pb[pointmtrindex]
           + (vol[2] / volume) * pc[pointmtrindex];
    }
  } else if (iloc == (int) ONEDGE) {
    pa = org(*searchtet);
    pb = dest(*searchtet);
    if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) {
      volume = distance(pa, pb);
      vol[0] = distance(searchpt, pb);
      vol[1] = distance(pa, searchpt);
      size = (vol[0] / volume) * pa[pointmtrindex]
           + (vol[1] / volume) * pb[pointmtrindex];
    }
  } else if (iloc == (int) ONVERTEX) {
    pa = org(*searchtet);
    if (pa[pointmtrindex] > 0) {
      size = pa[pointmtrindex];
    }
  }

  return size;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interpolatemeshsize()    Interpolate the mesh size from a background mesh //
//                          (source) to the current mesh (destination).      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::interpolatemeshsize()
{
  triface searchtet;
  point ploop;
  REAL minval = 0.0, maxval = 0.0;
  int iloc;
  int count;

  if (!b->quiet) {
    printf("Interpolating mesh size ...\n");
  }

  long bak_nonregularcount = nonregularcount;
  nonregularcount = 0l; // Count the number of (slow) global searches.
  long baksmaples = bgm->samples;
  bgm->samples = 3l;
  count = 0; // Count the number of interpolated points.

  // Interpolate sizes for all points in the current mesh.
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != nullptr) {
    // Search a tet in bgm which containing this point.
    searchtet.tet = nullptr;
    iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1
    if (iloc != (int) OUTSIDE) {
      // Interpolate the mesh size.
      ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc);
      setpoint2bgmtet(ploop, bgm->encode(searchtet));
      if (count == 0) {
        // This is the first interpolated point.
        minval = maxval = ploop[pointmtrindex];
      } else {
        if (ploop[pointmtrindex] < minval) {
          minval = ploop[pointmtrindex];
        }
        if (ploop[pointmtrindex] > maxval) {
          maxval = ploop[pointmtrindex];
        }
      }
      count++;
    } else {
      if (!b->quiet) {
        printf("Warnning:  Failed to locate point %d in source mesh.\n",
               pointmark(ploop));
      }
    }
    ploop = pointtraverse();
  }

  if (b->verbose) {
    printf("  Interoplated %d points.\n", count);
    if (nonregularcount > 0l) {
      printf("  Performed %ld brute-force searches.\n", nonregularcount);
    }
    printf("  Size rangle [%.17g, %.17g].\n", minval, maxval);
  }

  bgm->samples = baksmaples;
  nonregularcount = bak_nonregularcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertconstrainedpoints()    Insert a list of points into the mesh.       //
//                                                                           //
// Assumption:  The bounding box of the insert point set should be no larger //
// than the bounding box of the mesh.  (Required by point sorting).          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen)
{
  triface searchtet, spintet;
  face splitsh;
  face splitseg;
  insertvertexflags ivf;
  flipconstraints fc;
  int randflag = 0;
  int t1ver;
  int i;

  if (b->verbose) {
    printf("  Inserting %d constrained points\n", arylen);
  }

  if (b->no_sort) { // -b/1 option.
    if (b->verbose) {
      printf("  Using the input order.\n"); 
    }
  } else {
    if (b->verbose) {
      printf("  Permuting vertices.\n"); 
    }
    point swappoint;
    int randindex;
    srand(arylen);
    for (i = 0; i < arylen; i++) {
      randindex = rand() % (i + 1); 
      swappoint = insertarray[i];
      insertarray[i] = insertarray[randindex];
      insertarray[randindex] = swappoint;
    }
    if (b->brio_hilbert) { // -b1 option
      if (b->verbose) {
        printf("  Sorting vertices.\n"); 
      }
      hilbert_init(in->mesh_dim);
      int ngroup = 0; 
      brio_multiscale_sort(insertarray, arylen, b->brio_threshold, 
                           b->brio_ratio, &ngroup);
    } else { // -b0 option.
      randflag = 1;
    } // if (!b->brio_hilbert)
  } // if (!b->no_sort)

  long bak_nonregularcount = nonregularcount;
  nonregularcount = 0l;
  long baksmaples = samples;
  samples = 3l; // Use at least 3 samples. Updated in randomsample().

  long bak_seg_count = st_segref_count;
  long bak_fac_count = st_facref_count;
  long bak_vol_count = st_volref_count;

  // Initialize the insertion parameters. 
  if (b->incrflip) { // -l option
    // Use incremental flip algorithm.
    ivf.bowywat = 0; 
    ivf.lawson = 1;
    ivf.validflag = 0; // No need to validate the cavity.
    fc.enqflag = 2;
  } else {
    // Use Bowyer-Watson algorithm.
    ivf.bowywat = 1; 
    ivf.lawson = 0;
    ivf.validflag = 1; // Validate the B-W cavity.
  }
  ivf.rejflag = 0;  // Do not check encroachment.
  if (b->metric) { // -m option.
    ivf.rejflag |= 4; // Reject it if it lies in some protecting balls.
  }
  ivf.chkencflag = 0; 
  ivf.sloc = (int) INSTAR;
  ivf.sbowywat = 3; 
  ivf.splitbdflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric;

  // Insert the points.
  for (i = 0; i < arylen; i++) {
    // Find the location of the inserted point.
    // Do not use 'recenttet', since the mesh may be non-convex.
    searchtet.tet = nullptr; 
    ivf.iloc = scoutpoint(insertarray[i], &searchtet, randflag);

    // Decide the right type for this point.
    setpointtype(insertarray[i], FREEVOLVERTEX); // Default.
    splitsh.sh = nullptr;
    splitseg.sh = nullptr;
    if (ivf.iloc == (int) ONEDGE) {
      if (issubseg(searchtet)) {
        tsspivot1(searchtet, splitseg);
        setpointtype(insertarray[i], FREESEGVERTEX);
        //ivf.rejflag = 0;
      } else {
        // Check if it is a subface edge.
        spintet = searchtet;
        while (1) {
          if (issubface(spintet)) {
            tspivot(spintet, splitsh);
            setpointtype(insertarray[i], FREEFACETVERTEX);
            //ivf.rejflag |= 1;
            break;
          }
          fnextself(spintet);
          if (spintet.tet == searchtet.tet) break;
        }
      }
    } else if (ivf.iloc == (int) ONFACE) {
      if (issubface(searchtet)) {
        tspivot(searchtet, splitsh);
        setpointtype(insertarray[i], FREEFACETVERTEX);
        //ivf.rejflag |= 1;
      }
    }

    // Now insert the point.
    if (insertpoint(insertarray[i], &searchtet, &splitsh, &splitseg, &ivf)) {
      if (flipstack != nullptr) {
        // There are queued faces. Use flips to recover Delaunayness.
        lawsonflip3d(&fc);
        // There may be unflippable edges. Ignore them.
        unflipqueue->restart();
      }
      // Update the Steiner counters.
      if (pointtype(insertarray[i]) == FREESEGVERTEX) {
        st_segref_count++;
      } else if (pointtype(insertarray[i]) == FREEFACETVERTEX) {
        st_facref_count++;
      } else {
        st_volref_count++;
      }
    } else {
      // Point is not inserted.
      if (ivf.iloc == (int) OUTSIDE) {
        if (!b->quiet) {
          printf("Warning:  Point #%d lies outside the domain. Ignored.\n",
                 pointmark(insertarray[i]) - in->numberofpoints);
        }
      } else if (ivf.iloc == (int) ONVERTEX) {
        if (!b->quiet) {
          printf("Warning:  Point #%d is coincident with Point #%d. Ignored.\n",
                 pointmark(insertarray[i]) - in->numberofpoints,
                 pointmark(org(searchtet)));
        }
      } else if (ivf.iloc == (int) NEARVERTEX) {
        if (!b->quiet) {
          printf("Warning:  Point #%d is too close to Point #%d. Rejected.\n",
                 pointmark(insertarray[i]) - in->numberofpoints,
                 pointmark(point2ppt(insertarray[i])));
        }
      } else if (ivf.iloc == (int) ENCVERTEX) {
        if (!b->quiet) {
          printf("Warning:  Point #%d encroaches upon Point #%d. Rejected.\n",
                 pointmark(insertarray[i]) - in->numberofpoints,
                 pointmark(point2ppt(insertarray[i])));
        }
      } else {
        if (!b->quiet) {
            printf("Warning:  Failed to insert Point #%d (%g,%g,%g).Ignored.\n", 
                   i, insertarray[i][0], insertarray[i][1], insertarray[i][2]);
        }
      }
      //pointdealloc(insertarray[i]);
      setpointtype(insertarray[i], UNUSEDVERTEX);
      unuverts++;
    }
  } // i

  if (b->verbose) {
    printf("  Inserted %ld (%ld, %ld, %ld) vertices.\n", 
           st_segref_count + st_facref_count + st_volref_count - 
           (bak_seg_count + bak_fac_count + bak_vol_count),
           st_segref_count - bak_seg_count, st_facref_count - bak_fac_count,
           st_volref_count - bak_vol_count);
    if (nonregularcount > 0l) {
      printf("  Performed %ld brute-force searches.\n", nonregularcount);
    }
  }

  nonregularcount = bak_nonregularcount;
  samples = baksmaples; 
}

void tetgenmesh::insertconstrainedpoints(tetgenio *addio)
{
  point *insertarray, newpt;
  REAL x, y, z, w;
  int index, attribindex, mtrindex;
  int arylen, i, j;

  if (!b->quiet) {
    printf("Inserting constrained points ...\n");
  }

  insertarray = new point[addio->numberofpoints];
  arylen = 0;
  index = 0;
  attribindex = 0;
  mtrindex = 0;

  for (i = 0; i < addio->numberofpoints; i++) {
    x = addio->pointlist[index++];
    y = addio->pointlist[index++];
    z = addio->pointlist[index++];
    // Test if this point lies inside the bounding box.
    if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax) ||
        (z < zmin) || (z > zmax)) {
      if (b->verbose) {
        printf("Warning:  Point #%d lies outside the bounding box. Ignored\n",
               i + in->firstnumber);
      }
      continue;
    }
    makepoint(&newpt, UNUSEDVERTEX);
    newpt[0] = x;
    newpt[1] = y;
    newpt[2] = z;
    // Read the point attributes. (Including point weights.)
    for (j = 0; j < addio->numberofpointattributes; j++) {
      newpt[3 + j] = addio->pointattributelist[attribindex++];
    }
    // Read the point metric tensor.
    for (j = 0; j < addio->numberofpointmtrs; j++) {
      newpt[pointmtrindex + j] = addio->pointmtrlist[mtrindex++];
    }
    if (b->weighted) { // -w option
      if (addio->numberofpointattributes > 0) {
        // The first point attribute is its weight.
        w = newpt[3];
      } else {
        // No given weight available. Default choose the maximum
        //   absolute value among its coordinates.        
        w = fabs(x);
        if (w < fabs(y)) w = fabs(y);
        if (w < fabs(z)) w = fabs(z);
      }
      if (b->weighted_param == 0) {
        newpt[3] = x * x + y * y + z * z - w; // Weighted DT.
      } else { // -w1 option
        newpt[3] = w;  // Regular tetrahedralization.
      }
    }
    insertarray[arylen] = newpt;
    arylen++;
  } // i

  // Insert the points.
  insertconstrainedpoints(insertarray, arylen);

  delete [] insertarray;
}

////                                                                       ////
////                                                                       ////
//// reconstruct_cxx //////////////////////////////////////////////////////////

//// refine_cxx ///////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// marksharpsegments()    Mark sharp segments.                               //
//                                                                           //
// A segment is SHARP if there are two facets intersecting at it with an     //
// internal dihedral angle (*) less than an angle \theta.                    //
//                                                                           //
// A theoretical value of \theta is arccos(1/3) \approx 70.54 degree.  It is //
// possible to relax it in practice. Here we choose \theta = 65 degree.      //
//                                                                           //
// The minimum dihedral angle between facets (minfacetdihed) is calulcated.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::marksharpsegments()
{
  triface starttet, spintet, adjtet;
  face startsh, spinsh, neighsh;
  face segloop, nextseg, prevseg;
  point eorg, edest;
  REAL ang, smallang;
  bool issharp;
  int interiorflag;
  int t1ver;
  int sharpcount;

  // For storing extremely small dihedral angle.
  face *parysh;
  REAL exsmallang;
  int exsharpcount;

  if (b->verbose > 0) {
    printf("  Marking sharp segments.\n");
  }

  minfacetdihed = PI;
  smallang = 65.0 * PI / 180.0; // 65 degree.
  exsmallang = 5.0 * PI / 180.0; // 5 degree.
  sharpcount = exsharpcount = 0;

  // A segment s may have been split into many subsegments. Operate the one
  //   which contains the origin of s. Then mark the rest of subsegments.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) nullptr) {
    segloop.shver = 0;
    senext2(segloop, prevseg);
    spivotself(prevseg);
    if (prevseg.sh == nullptr) {
      // Operate on this seg s.
      issharp = false;
      spivot(segloop, startsh);
      if (startsh.sh != nullptr) {
        eorg = sorg(segloop);
        edest = sdest(segloop);
        if (sorg(startsh) != eorg) sesymself(startsh);
        stpivot(startsh, starttet);
        if (starttet.tet != nullptr) {
          spinsh = startsh;
          spintet = starttet;
          while (1) {
            fnextself(spintet);
            if (issubface(spintet)) {
              tspivot(spintet, neighsh);
              if (neighsh.sh != spinsh.sh) {
                // Only do test when the adjacent tet lies in the interior.
                interiorflag = 0;
                stpivot(spinsh, adjtet);
                if (!ishulltet(adjtet)) {
                  if (b->convex) { // -c option.
                    if (elemattribute(adjtet.tet, numelemattrib - 1) != -1.0) {
                      interiorflag = 1;
                    }
                  } else {
                    interiorflag = 1;
                  }
                }
                if (interiorflag) {
                  ang = facedihedral(eorg,edest,sapex(spinsh),sapex(neighsh));
                  // Only do check if a sharp angle has not been found.
                  if (!issharp) issharp = (ang < smallang);
                  // Remember the smallest facet dihedral angle.
                  minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang;
                  if (ang < exsmallang) {
                    // It's an extremely small dihedral angle.
                    // Mark the two facets. 
                    // To avoid too many Steiner points, do not refine them.
                    if (shelltype(spinsh) != SHARP) {
                      setshelltype(spinsh, SHARP);
                      cavesegshlist->newindex((void **) &parysh);
                      *parysh = spinsh;
                    }
                    if (shelltype(neighsh) != SHARP) {                 
                      setshelltype(neighsh, SHARP);
                      cavesegshlist->newindex((void **) &parysh);
                      *parysh = neighsh;
                    }
                    exsharpcount++;
                  }
                } // if (interiorflag)
                spinsh = neighsh;
                if (sorg(spinsh) != eorg) sesymself(spinsh);
              } // if (neighsh.sh != spinsh.sh)
            }
            if (spintet.tet == starttet.tet) break;
          } // while (1)
        } // if (starttet.tet != nullptr)
      } // if (startsh.sh != nullptr)
      if (issharp) {
        if (b->verbose > 2) {
          printf("      Mark a sharp segment (%d, %d).\n",
                 pointmark(eorg), pointmark(edest));
        }
        setshelltype(segloop, SHARP);
        // The endpoint of this segment is acute.
        if (pointtype(eorg) == RIDGEVERTEX) {
          setpointtype(eorg, ACUTEVERTEX);
        } else {
          assert(pointtype(eorg) == ACUTEVERTEX); // SELF_CHECK
        }
        // Set the type for all subsegments at forwards.
        edest = sdest(segloop);
        senext(segloop, nextseg);
        spivotself(nextseg);
        while (nextseg.sh != nullptr) {
          setshelltype(nextseg, SHARP);
          // Adjust the direction of nextseg.
          nextseg.shver = 0;
          if (sorg(nextseg) != edest) {
            sesymself(nextseg);
          }
          assert(sorg(nextseg) == edest);
          edest = sdest(nextseg);
          // Go the next connected subsegment at edest.
          senextself(nextseg);
          spivotself(nextseg);
        }
        // The endpoint of this segment is acute.
        if (pointtype(edest) == RIDGEVERTEX) {
          setpointtype(edest, ACUTEVERTEX);
        } else {
          assert(pointtype(edest) == ACUTEVERTEX); // SELF_CHECK
        }
        sharpcount++;
      } // if (issharp)
    } // if (prevseg.sh == nullptr)
    segloop.sh = shellfacetraverse(subsegs);
  }

  // Mark all facets at extremely small dihedral angles.
  if (cavesegshlist->objects > 0) {
    face *parysh1;
    int i, j, k;

    for (i = 0; i < cavesegshlist->objects; i++) {
      parysh = (face *) fastlookup(cavesegshlist, i);
      caveshlist->newindex((void **) &parysh1);
      *parysh1 = *parysh;
      for (j = 0; j < caveshlist->objects; j++) {
        parysh1 = (face *) fastlookup(caveshlist, j);
        spinsh = *parysh1;
        for (k = 0; k < 3; k++) {
          sspivot(spinsh, nextseg);
          if (nextseg.sh == nullptr) {
            spivot(spinsh, neighsh);
            if (shelltype(neighsh) != SHARP) {                 
              setshelltype(neighsh, SHARP);
              caveshlist->newindex((void **) &parysh1);
              *parysh1 = neighsh;
            }
          }
          senextself(spinsh);
        } // k
      } // j
      caveshlist->restart();
    } // i
    cavesegshlist->restart();
  } // if (cavesegshlist->objects > 0)

  if (b->verbose) {
    if (sharpcount > 0) {
      printf("  Found %d (%d) sharp segments.\n", sharpcount, exsharpcount);
    }
    printf("  Minimum fac-fac angle = %g.\n", minfacetdihed / PI * 180.0);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// decidefeaturepointsizes()    Calculate sizes for all feature points.      //
//                                                                           //
// A feature point is either an acute vertex or a Steiner point on a sharp   //
// segment.  Each feature point p will be protected by a ball whose radius   //
// is called its "feature size".                                             //
//                                                                           //
// NOTE: we should have already marked all features points in the two func-  //
// tions: markacutevertices() and marksharpsegments().  Each feature point   //
// has the type ACUTEVERTEX or FREESEGVERTEX.                                //
//                                                                           //
// The feature size of a vertex is the minimum of the following sizes:       //
//   (0) the (approximated) local feature size (the distance to the second   //
//       nearest boundary) of the vertex;
//   (1) the value specified in .mtr file (-m option);                       //
//   (2) the cubic root of a fixed maximal volume constraint ('-a__');       //
//   (3) the cubic root of a maximal volume constraint in a region ('-a');   //
//   (4) the square root of a maximal area constraint in a .var file;        //
//   (5) a maximal length constraint in a .var file;                         //
//                                                                           //
// If 'b->nobisect' ('-Y' option) is set, every input vertex has a size. It  //
// is used to prevent creating too close Steiner points.                     //
//                                                                           //
// The feature size of a Steiner point is linearly interpolated from its adj-//
// acent vertices which belong to the "carrier" (the boundary of the lowrest //
// dimension) of this Steiner point.  For example, a Steiner point on a seg- //
// ment gets its size from the two endpoints of the segment.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::decidefeaturepointsizes()
{
  arraypool *tetlist, *verlist;
  triface starttet, *parytet;
  face checksh, parentsh, shloop;
  face checkseg, prevseg, nextseg, testseg;
  point ploop, adjpt, e1, e2, *parypt;
  REAL lfs_0, lfs_1, lfs_2;
  REAL len, vol, maxlen = 0.0, varlen;
  REAL ang, a, a1, a2, a3, prjpt[3], n[3];
  int featureflag, featurecount;
  int i, j;

  if (b->verbose > 0) {
    printf("  Deciding feature-point sizes.\n");
  }

  // Initialize working lists.
  tetlist = cavetetlist;
  verlist = cavetetvertlist;

  if (b->fixedvolume) {
    // A fixed volume constraint is imposed. This gives an upper bound of
    //   the maximal radius of the protect ball of a vertex.
    maxlen = pow(6.0 * b->maxvolume, 1.0 / 3.0);
  }

  // First, assign a size of p if p is a feature point or an input point and 
  //   the -Y option is used.
  featurecount = 0;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) nullptr) {
    // Check if it is a feature point.
    featureflag = 0;
    // Only calculate the size if it has a size zero.
    // The point may already has a positive size (-m option).
    if (ploop[pointmtrindex] == 0) {
      if (pointtype(ploop) == ACUTEVERTEX) {
        featureflag = 1;
      } else {
        if (b->nobisect) { // '-Y' option
          if ((pointtype(ploop) == RIDGEVERTEX) ||
              (pointtype(ploop) == FACETVERTEX) ||
              (pointtype(ploop) == VOLVERTEX)) {
            featureflag = 1;  // It is an input vertex.
          }
        }
      }
    }
    if (featureflag) {
      // Form star(p).
      getvertexstar(1, ploop, tetlist, verlist, nullptr);
      // Calculate lfs_0(p), i.e., the smallest distance from p to a vertex.
      // We approximate it by taking the distance of p to its nearest
      //   vertex in Link(p).
      lfs_0 = longest;
      for (i = 0; i < verlist->objects; i++) {
        parypt = (point *) fastlookup(verlist, i);
        adjpt = * parypt;
        if (adjpt == dummypoint) {
          continue; // Skip a dummypoint.
        }
        if (pointtype(adjpt) == FREESEGVERTEX) {
          // A Steiner point. Get the subsegment.
          sdecode(point2sh(adjpt), checkseg);
          assert(checkseg.sh != nullptr);
          checkseg.shver = 0;
          if (sdest(checkseg) != adjpt) sesymself(checkseg);
          assert(sdest(checkseg) == adjpt);
          // It is possible that the original segment of 'adjpt' does not
          //   have 'ploop' as an endpoint.
          if (sorg(checkseg) == ploop) {
            // Find the other end point of the original segment.
            nextseg = checkseg;
            while (1) {
              senext(nextseg, testseg);
              spivotself(testseg);
              if (testseg.sh == nullptr) break;
              // Go to the next subseg.
              nextseg = testseg;
              // Adjust the direction of the nextseg.
              nextseg.shver = 0;
              if (sorg(nextseg) != adjpt) sesymself(nextseg);
              assert(sorg(nextseg) == adjpt);
              adjpt = sdest(nextseg);
            }
          }
            } else if (pointtype(adjpt) == FREEFACETVERTEX) {
          // Ignore a Steiner point on facet.
          continue;
        } else if (pointtype(adjpt) == FREEVOLVERTEX) {
          // Ignore a Steiner point in volume.
          continue;
        }  
        len = distance(ploop, adjpt);
        if (lfs_0 > len) lfs_0 = len;
      } // i
      assert(lfs_0 < longest); // SELF_CHECK
      ploop[pointmtrindex] = lfs_0;
      // Calculate lfs_1(p), i.e., the smallest distance from p to a segment.
      //   We approximate it by restricting the segments in Link(p).
      lfs_1 = lfs_0;
      for (i = 0; i < tetlist->objects; i++) {
        parytet = (triface *) fastlookup(tetlist, i);
        for (j = 0; j < 3; j++) {
          if (issubseg(*parytet)) {
            tsspivot1(*parytet, checkseg);
            e1 = sorg(checkseg);
            e2 = sdest(checkseg);
            // Only do calculation if the projeciton of 'p' lies inside the
            //   segment [e1, e2].
            ang = interiorangle(ploop, e1, e2, nullptr);
            ang *= 2.0;
            if (ang > PI) { 
              len = shortdistance(ploop, e1, e2);
              if (lfs_1 > len) {
                lfs_1 = len;
              }
            }
          }
          enextself(*parytet);
        } // j
      } // i
      if (ploop[pointmtrindex] > lfs_1) {
        ploop[pointmtrindex] = lfs_1;
      }
      // Calculate lfs_2(p), i.e., the smallest distance from p to a facet.
      //   We approximate it by restricting the facets in Link(p).
      lfs_2 = lfs_0; 
      for (i = 0; i < tetlist->objects; i++) {
        parytet = (triface *) fastlookup(tetlist, i);
        if (issubface(*parytet)) {
          tspivot(*parytet, checksh);
          adjpt = sorg(checksh);
          e1 = sdest(checksh);
          e2 = sapex(checksh);
          // Only do calculation if the projeciton of 'p' lies inside the
          //   subface [adjpt, e1, e2].
          projpt2face(ploop, adjpt, e1, e2, prjpt);
          facenormal(adjpt, e1, e2, n, 1, nullptr);
          a = sqrt(dot(n, n)); // area of [adjpt, e1, e2].
          if (a > 0) {
            facenormal(adjpt, e1, prjpt, n, 1, nullptr);
            a1 = sqrt(dot(n, n));
            facenormal(e1, e2, prjpt, n, 1, nullptr);
            a2 = sqrt(dot(n, n));
            facenormal(e2, adjpt, prjpt, n, 1, nullptr);
            a3 = sqrt(dot(n, n));
            if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) {
              len = distance(ploop, prjpt);
              if (lfs_2 > len) {
                lfs_2 = len;
              }
            }
          } else {
            assert(0); // a degenerate triangle.
          } // if (a > 0)
        }
      }
      if (ploop[pointmtrindex] > lfs_2) {
        ploop[pointmtrindex] = lfs_2;
      }
      if (b->fixedvolume) {
        // A fixed volume constraint is imposed. Adjust H(p) <= maxlen.
        if (ploop[pointmtrindex] > maxlen) {
          ploop[pointmtrindex] = maxlen;
        }
      }
      if (b->varvolume) {
        // Variant volume constraints are imposed. Adjust H(p) <= varlen.
        for (i = 0; i < tetlist->objects; i++) {
          parytet = (triface *) fastlookup(tetlist, i);
          starttet = *parytet;
          vol = volumebound(starttet.tet);
          if (vol > 0.0) {
            varlen = pow(6 * vol, 1.0 / 3.0);
            if (ploop[pointmtrindex] > varlen) {
              ploop[pointmtrindex] = varlen;
            }
          }
        }
      }
      // The size is calculated.
      assert(ploop[pointmtrindex] > 0); // SELF_CHECK
      // Clear working lists.
      tetlist->restart();
      verlist->restart();
      featurecount++;
    } // if (featureflag)
    ploop = pointtraverse();
  }

  if (b->verbose) {
    printf("  %d feature points.\n", featurecount);
  }

  // Second only assign sizes for all Steiner points which were inserted on
  //   sharp segments. The sizes are interpolated from the endpoints of
  //   the segments.
  featurecount = 0;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) nullptr) {
    if (ploop[pointmtrindex] == 0.0) {
      if (pointtype(ploop) == FREESEGVERTEX) {
        // A Steiner point on segment.
        featureflag = 0;
        sdecode(point2sh(ploop), checkseg);
        assert(checkseg.sh != nullptr);
        checkseg.shver = 0;
        e1 = farsorg(checkseg);  // The origin of this seg.        
        e2 = farsdest(checkseg); // The dest of this seg.      
        if (b->nobisect) { // '-Y' option.
          featureflag = 1;
        } else {
          if ((e1[pointmtrindex] > 0) && (e2[pointmtrindex] > 0)) {
            featureflag = 1;
          }
        }
        if (featureflag) {
          len = distance(e1, e2);
          lfs_0 = distance(e1, ploop); // Re-use lfs_0.
          ploop[pointmtrindex] = e1[pointmtrindex]
            + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]);
          featurecount++;
        } // if (featureflag)
      } 
    } // if (ploop[pointmtrindex] == 0.0)
    ploop = pointtraverse();
  }

  if (b->verbose && (featurecount > 0)) {
    printf("  %d Steiner feature points.\n", featurecount);
  }

  if (checkconstraints) {
    // A .var file exists. Adjust feature sizes. And make sure that every
    //   corner of a constraining facet get a size.
    if (in->facetconstraintlist) {
      // Have facet area constrains.
      subfaces->traversalinit();
      shloop.sh = shellfacetraverse(subfaces);
      while (shloop.sh != (shellface *) nullptr) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the three corners are feature points.
          varlen = sqrt(varlen);
          for (j = 0; j < 3; j++) {
            ploop = (point) shloop.sh[3 + j];
            if (ploop[pointmtrindex] > 0) {
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            } else {
              // This corner has no size yet. Set it.
              ploop[pointmtrindex] = varlen;
            }
          } // j
        }
        shloop.sh = shellfacetraverse(subfaces);
      }
    }
    if (in->segmentconstraintlist) {
      // Have facet area constrains.
      subsegs->traversalinit();
      shloop.sh = shellfacetraverse(subsegs);
      while (shloop.sh != (shellface *) nullptr) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the two endpoints are feature points.
          for (j = 0; j < 2; j++) {
            ploop = (point) shloop.sh[3 + j];
            if (ploop[pointmtrindex] > 0.0) {
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            } else {
              ploop[pointmtrindex] = varlen;
            }
          } // j
        }
        shloop.sh = shellfacetraverse(subsegs);
      }
    }
  } // if (checkconstraints)
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkseg4encroach()    Check if an edge is encroached upon by a point.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt)
{
  // Check if the point lies inside the diametrical sphere of this seg. 
  REAL v1[3], v2[3];

  v1[0] = pa[0] - checkpt[0];
  v1[1] = pa[1] - checkpt[1];
  v1[2] = pa[2] - checkpt[2];
  v2[0] = pb[0] - checkpt[0];
  v2[1] = pb[1] - checkpt[1];
  v2[2] = pb[2] - checkpt[2];

  if (dot(v1, v2) < 0) {
    // Inside.
    if (b->metric || b->nobisect) { // -m or -Y option.
      if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) {
        // The projection of 'checkpt' lies inside the segment [a,b].
        REAL prjpt[3], u, v, t;
        projpt2edge(checkpt, pa, pb, prjpt);
        // Interoplate the mesh size at the location 'prjpt'.
        u = distance(pa, pb);
        v = distance(pa, prjpt);
        t = v / u;
        // 'u' is the mesh size at 'prjpt'
        u = pa[pointmtrindex] + t * (pb[pointmtrindex] - pa[pointmtrindex]);
        v = distance(checkpt, prjpt);
        if (v < u) {
          return 1; // Encroached prot-ball!
        }
      } else {
        return 1; // NO protecting ball. Encroached.
      }
    } else {
      return 1; // Inside! Encroached.
    }
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkseg4split()    Check if we need to split a segment.                  //
//                                                                           //
// A segment needs to be split if it is in the following case:               //
//  (1) It is encroached by an existing vertex.                              //
//  (2) It has bad quality (too long).                                       //
//  (3) Its length is larger than the mesh sizes at its endpoints.           //
//                                                                           //
// Return 1 if it needs to be split, otherwise, return 0.  'pencpt' returns  //
// an encroaching point if there exists. 'qflag' returns '1' if the segment  //
// has a length larger than the desired edge length.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag)
{
  REAL ccent[3], len, r;
  int i;

  point forg = sorg(*chkseg);
  point fdest = sdest(*chkseg);

  // Initialize the return values.
  encpt = nullptr;
  qflag = 0;

  len = distance(forg, fdest);
  r = 0.5 * len;
  for (i = 0; i < 3; i++) {
    ccent[i] = 0.5 * (forg[i] + fdest[i]);
  }

  // First check its quality.
  if (checkconstraints && (areabound(*chkseg) > 0.0)) {
    if (len > areabound(*chkseg)) {
      qflag = 1;
      return 1;
    }
  }

  if (b->fixedvolume) {
    if ((len * len * len) > b->maxvolume) {
      qflag = 1;
      return 1;
    }
  }

  if (b->metric) { // -m option. Check mesh size. 
    // Check if the ccent lies outside one of the prot.balls at vertices.
    if (((forg[pointmtrindex] > 0) && (r > forg[pointmtrindex])) ||
        ((fdest[pointmtrindex]) > 0 && (r > fdest[pointmtrindex]))) {
      qflag = 1; // Enforce mesh size.
      return 1;
    }
  }


  // Second check if it is encroached.
  triface searchtet, spintet;
  point eapex;
  REAL d, diff;
  int t1ver;

  sstpivot1(*chkseg, searchtet);
  spintet = searchtet;
  while (1) {
    eapex = apex(spintet);
    if (eapex != dummypoint) {
      d = distance(ccent, eapex);
      diff = d - r;
      if (fabs(diff) / r < b->epsilon) diff = 0.0; // Rounding.
      if (diff < 0) {
        // This segment is encroached by eapex.
        encpt = eapex;
        break;
      }
    }
    fnextself(spintet);
    if (spintet.tet == searchtet.tet) break;
  } // while (1)

  if (encpt != nullptr) {
    return 1;
  }

  return 0; // No need to split it.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsegment()    Split a segment.                                        //
//                                                                           //
// The segment 'splitseg' is intended to be split. It will be split if it    //
// is in one of the following cases:                                         //
//   (1) It is encroached by an existing vertex 'encpt != nullptr'; or          //
//   (2) It is in bad quality 'qflag == 1'; or                               //
//   (3) Its length is larger than the mesh sizes at its endpoints.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, 
                             int chkencflag)
{
  point pa = sorg(*splitseg);
  point pb = sdest(*splitseg);


  if (qflag == 0) {
    // Quickly check if we CAN split this segment.
    //if (encpt == nullptr) {
      // Do not split this segment if the length is smaller than the mesh
      //   size at one of its endpoints. 
      REAL len = distance(pa, pb);
      if ((len < pa[pointmtrindex]) || (len < pb[pointmtrindex])) {
        return 0;
      }
    //}
  }

  if (b->nobisect) { // With -Y option.
    // Only split this segment if it is allowed to be split.
    if (checkconstraints) {
      // Check if it has a non-zero length bound. 
      if (areabound(*splitseg) == 0) {
        // It is not allowed.  However, if all facets containing this seg
        //   are allowed to be split, we still split it.
        face parentsh, spinsh;
        //splitseg.shver = 0;
        spivot(*splitseg, parentsh);
        if (parentsh.sh == nullptr) {
          return 0; // A dangling segment. Do not split it.
        }
        spinsh = parentsh;
        while (1) {
          if (areabound(spinsh) == 0) break;
          spivotself(spinsh);
          if (spinsh.sh == parentsh.sh) break;
        }
        if (areabound(spinsh) == 0) {
          // Not all facets at this seg are allowed to be split.
          return 0;  // Do not split it.
        }
      }
    } else {
      return 0; // Do not split this segment.
    }
  } // if (b->nobisect)

  triface searchtet;
  face searchsh;
  point newpt;
  insertvertexflags ivf;

  makepoint(&newpt, FREESEGVERTEX);
  getsteinerptonsegment(splitseg, encpt, newpt);

  // Split the segment by the Bowyer-Watson algorithm.
  sstpivot1(*splitseg, searchtet);
  ivf.iloc = (int) ONEDGE;
  // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces;
  ivf.bowywat = 3;
  ivf.validflag = 1; // Validate the B-W cavity.
  ivf.lawson = 2; // Do flips to recover Delaunayness.
  ivf.rejflag = 0;     // Do not check encroachment of new segments/facets.
  if ((encpt == nullptr) && (qflag == 0)) {
    ivf.rejflag |= 4;  // Do check encroachment of protecting balls.
  }
  ivf.chkencflag = chkencflag;
  ivf.sloc = (int) INSTAR; // ivf.iloc;
  ivf.sbowywat = 3; // ivf.bowywat;  // Surface mesh options.
  ivf.splitbdflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = 1;


  if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) {
    st_segref_count++;
    if (steinerleft > 0) steinerleft--;
    if (flipstack != nullptr) {
      flipconstraints fc;
      fc.chkencflag = chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      unflipqueue->restart();
    }
    return 1;
  } else {
    // Point is not inserted.
    pointdealloc(newpt);
    return 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencsegs()    Repair encroached (sub) segments.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencsegs(int chkencflag)
{
  face *bface;
  point encpt = nullptr;
  int qflag = 0;

  // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubsegs->items > 0) && (steinerleft != 0)) {
    badsubsegs->traversalinit();
    bface = (face *) badsubsegs->traverse();
    while ((bface != nullptr) && (steinerleft != 0)) {
      // Skip a deleleted element.
      if (bface->shver >= 0) {
        // A queued segment may have been deleted (split).
        if ((bface->sh != nullptr) && (bface->sh[3] != nullptr)) {
          // A queued segment may have been processed. 
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
            if (checkseg4split(bface, encpt, qflag)) {
              splitsegment(bface, encpt, qflag, chkencflag);
            }
          }
        }
        // Remove this entry from list.
        bface->shver = -1; // Signal it as a deleted element.
        badsubsegs->dealloc((void *) bface);
      }
      bface = (face *) badsubsegs->traverse();
    }
  }

  if (badsubsegs->items > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else {
      assert(0); // Unknown case.
    }
    badsubsegs->traversalinit();
    bface = (face *) badsubsegs->traverse();
    while (bface  != nullptr) {
      // Skip a deleleted element.
      if (bface->shver >= 0) {
        if ((bface->sh != nullptr) && (bface->sh[3] != nullptr)) {
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
          }
        }
      }
      bface = (face *) badsubsegs->traverse();
    }
    badsubsegs->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueuesubface()    Queue a subface or a subsegment for encroachment chk. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface)
{
  if (!smarktest2ed(*chkface)) {
    smarktest2(*chkface); // Only queue it once.
    face *queface = (face *) pool->alloc();
    *queface = *chkface;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkfac4encroach()    Check if a subface is encroached by a point.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt,
                                  REAL* cent, REAL* r)
{
  REAL rd, len;

  circumsphere(pa, pb, pc, nullptr, cent, &rd);
  assert(rd != 0);
  len = distance(cent, checkpt);
  if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding.
 
  if (len < rd) {
    // The point lies inside the circumsphere of this face.
    if (b->metric || b->nobisect) { // -m or -Y option.
      if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) &&
          (pc[pointmtrindex] > 0)) {
        // Get the projection of 'checkpt' in the plane of pa, pb, and pc.
        REAL prjpt[3], n[3];
        REAL a, a1, a2, a3;
        projpt2face(checkpt, pa, pb, pc, prjpt);
        // Get the face area of [a,b,c].
        facenormal(pa, pb, pc, n, 1, nullptr);
        a = sqrt(dot(n,n));
        // Get the face areas of [a,b,p], [b,c,p], and [c,a,p].
        facenormal(pa, pb, prjpt, n, 1, nullptr);
        a1 = sqrt(dot(n,n));
        facenormal(pb, pc, prjpt, n, 1, nullptr);
        a2 = sqrt(dot(n,n));
        facenormal(pc, pa, prjpt, n, 1, nullptr);
        a3 = sqrt(dot(n,n));
        if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) {
          // This face contains the projection.
          // Get the mesh size at the location of the projection point.
          rd = a1 / a * pc[pointmtrindex]
             + a2 / a * pa[pointmtrindex]
             + a3 / a * pb[pointmtrindex];
          len = distance(prjpt, checkpt);
          if (len < rd) {
            return 1; // Encroached.
          }
        }
      } else {
        return 1;  // No protecting ball. Encroached.
      }
    } else {
      *r = rd;
      return 1;  // Encroached.
    }
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkfac4split()    Check if a subface needs to be split.                 //
//                                                                           //
// A subface needs to be split if it is in the following case:               //
//  (1) It is encroached by an existing vertex.                              //
//  (2) It has bad quality (has a small angle, -q).                          //
//  (3) It's area is larger than a prescribed value (.var).                  //
//                                                                           //
// Return 1 if it needs to be split, otherwise, return 0.                    //
// 'chkfac' represents its longest edge.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, 
                               REAL *cent)
{
  point pa, pb, pc;
  REAL area, rd, len;
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i;

  encpt = nullptr;
  qflag = 0;

  pa = sorg(*chkfac);
  pb = sdest(*chkfac);
  pc = sapex(*chkfac);

  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)
  cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)

  area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].

  // Compute the right hand side vector b (3x1).
  rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b]
  rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c]
  rhs[2] = 0.0;

  // Solve the 3 by 3 equations use LU decomposition with partial 
  //   pivoting and backward and forward substitute.
  if (!lu_decmp(A, 3, indx, &D, 0)) {
    // A degenerate triangle. 
    return 0;  
  }

  lu_solve(A, 3, indx, rhs, 0);
  cent[0] = pa[0] + rhs[0];
  cent[1] = pa[1] + rhs[1];
  cent[2] = pa[2] + rhs[2];
  rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);

  if (checkconstraints && (areabound(*chkfac) > 0.0)) {
    // Check if the subface has too big area.
    if (area > areabound(*chkfac)) {
      qflag = 1;
      return 1;
    }
  }

  if (b->metric) { // -m option. Check mesh size. 
    // Check if the ccent lies outside one of the prot.balls at vertices.
    if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) ||
        ((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) ||
        ((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) {
      qflag = 1; // Enforce mesh size.
      return 1;
    }
  }

  triface searchtet;

  // Check if this subface is locally encroached.
  for (i = 0; i < 2; i++) {
    stpivot(*chkfac, searchtet);
    if (!ishulltet(searchtet)) {
      len = distance(oppo(searchtet), cent);
      if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding.
      if (len < rd) {
        encpt = oppo(searchtet);
        return 1;
      }
    }
    sesymself(*chkfac);
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubface()    Split a subface.                                        //
//                                                                           //
// The subface may be encroached, or in bad-quality. It is split at its cir- //
// cumcenter ('ccent'). Do not split it if 'ccent' encroaches upon any seg-  //
// ments. Instead, one of the encroached segments is split.  It is possible  //
// that none of the encorached segments can be split.                        //
//                                                                           //
// The return value indicates whether a new point is inserted (> 0) or not   //
// (= 0). Furthermore, it is inserted on an encorached segment (= 1) or in-  //
// side the facet (= 2).                                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag,
                             REAL *ccent, int chkencflag)
{
  point pa = sorg(*splitfac);
  point pb = sdest(*splitfac);
  point pc = sapex(*splitfac);


  // Quickly check if we CAN split this subface.
  if (qflag == 0) {
    // Do not split this subface if it forms a very small dihedral with
    //   another facet. Avoid creating too many Steiner points.
    if (shelltype(*splitfac) == SHARP) {
      return 0;
    }
    // Do not split this subface if the 'ccent' lies inside the protect balls
    //   of one of its vertices.
    REAL rd = distance(ccent, pa);
    if ((rd <= pa[pointmtrindex]) || (rd <= pb[pointmtrindex]) ||
        (rd <= pc[pointmtrindex])) {
      return 0;
    }
  }

  if (b->nobisect) { // With -Y option.
    if (checkconstraints) {
      // Only split if it is allowed to be split.
      // Check if this facet has a non-zero constraint.
      if (areabound(*splitfac) == 0) {
        return 0; // Do not split it.
      }
    } else {
      return 0;
    }
  } // if (b->nobisect)

  face searchsh;
  insertvertexflags ivf;
  point newpt;
  int i;

  // Initialize the inserting point.
  makepoint(&newpt, FREEFACETVERTEX);
  // Split the subface at its circumcenter.
  for (i = 0; i < 3; i++) newpt[i] = ccent[i];

  // Search a subface which contains 'newpt'.
  searchsh = *splitfac;
  // Calculate an above point. It lies above the plane containing
  //   the subface [a,b,c], and save it in dummypoint. Moreover,
  //   the vector cent->dummypoint is the normal of the plane.
  calculateabovepoint4(newpt, pa, pb, pc);
  //   Parameters: 'aflag' = 1, - above point exists.
  //   'cflag' = 0, - non-convex, check co-planarity of the result.
  //   'rflag' = 0, - no need to round the locating result.
  ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0);

  if (!((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE))) {
    pointdealloc(newpt);
    return 0;
  }


  triface searchtet;
  face *paryseg;
  int splitflag;

  // Insert the point.
  stpivot(searchsh, searchtet);
  //assert((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE));
  // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces;
  ivf.bowywat = 3; 
  ivf.lawson = 2;
  ivf.rejflag = 1; // Do check the encroachment of segments.
  if (qflag == 0) {
    ivf.rejflag |= 4; // Reject it if it encroached upon any vertex.
  }
  ivf.chkencflag = chkencflag;
  ivf.sloc = (int) INSTAR; // ivf.iloc;
  ivf.sbowywat = 3; // ivf.bowywat;
  ivf.splitbdflag = 1;
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = 1;

  ivf.refineflag = 2;
  ivf.refinesh = searchsh;


  if (insertpoint(newpt, &searchtet, &searchsh, nullptr, &ivf)) {
    st_facref_count++;
    if (steinerleft > 0) steinerleft--;
    if (flipstack != nullptr) {
      flipconstraints fc;
      fc.chkencflag = chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      unflipqueue->restart();
    }
    return 1;
  } else {
    // Point was not inserted.
    pointdealloc(newpt);
    if (ivf.iloc == (int) ENCSEGMENT) {
      // Select an encroached segment and split it.
      splitflag = 0;
      for (i = 0; i < encseglist->objects; i++) {
        paryseg = (face *) fastlookup(encseglist, i);
        if (splitsegment(paryseg, nullptr, qflag, chkencflag | 1)) {
          splitflag = 1; // A point is inserted on a segment.
          break;
        }
      }
      encseglist->restart();
      if (splitflag) {
        // Some segments may need to be repaired.
        repairencsegs(chkencflag | 1);
        // Queue this subface if it is still alive and not queued.
        //if ((splitfac->sh != nullptr) && (splitfac->sh[3] != nullptr)) {
        //  // Only queue it if 'qflag' is set.
        //  if (qflag) { 
        //    enqueuesubface(badsubfacs, splitfac);
        //  }
        //}
      }
      return splitflag;
    } else {
      return 0;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencfacs()    Repair encroached subfaces.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencfacs(int chkencflag)
{
  face *bface;
  point encpt = nullptr;
  int qflag = 0;
  REAL ccent[3];

  // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubfacs->items > 0) && (steinerleft != 0)) {
    badsubfacs->traversalinit();
    bface = (face *) badsubfacs->traverse();
    while ((bface != nullptr) && (steinerleft != 0)) {
      // Skip a deleted element.
      if (bface->shver >= 0) {
        // A queued subface may have been deleted (split).
        if ((bface->sh != nullptr) && (bface->sh[3] != nullptr)) {
          // A queued subface may have been processed. 
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
            if (checkfac4split(bface, encpt, qflag, ccent)) {
              splitsubface(bface, encpt, qflag, ccent, chkencflag);
            }
          }
        }
        bface->shver = -1; // Signal it as a deleted element.
        badsubfacs->dealloc((void *) bface); // Remove this entry from list.
      }
      bface = (face *) badsubfacs->traverse();
    }
  }

  if (badsubfacs->items > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else {
      assert(0); // Unknown case.
    }
    badsubfacs->traversalinit();
    bface = (face *) badsubfacs->traverse();
    while (bface  != nullptr) {
      // Skip a deleted element.
      if (bface->shver >= 0) {
        if ((bface->sh != nullptr) && (bface->sh[3] != nullptr)) {
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
          }
        }
      }
      bface = (face *) badsubfacs->traverse();
    }
    badsubfacs->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueuetetrahedron()    Queue a tetrahedron for quality check.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueuetetrahedron(triface *chktet)
{
  if (!marktest2ed(*chktet)) {
    marktest2(*chktet); // Only queue it once.
    triface *quetet = (triface *) badtetrahedrons->alloc();
    *quetet = *chktet;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4split()    Check if the tet needs to be split.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) 
{
  point pa, pb, pc, pd, *ppt;
  REAL vda[3], vdb[3], vdc[3];
  REAL vab[3], vbc[3], vca[3];
  REAL N[4][3], L[4], cosd[6], elen[6];
  REAL maxcosd, vol, volbnd, smlen, rd;
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i, j;

  qflag = 0;

  pd = (point) chktet->tet[7];
  if (pd == dummypoint) {
    return 0; // Do not split a hull tet.
  }

  pa = (point) chktet->tet[4];
  pb = (point) chktet->tet[5];
  pc = (point) chktet->tet[6];

  // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c.
  // Set the matrix A = [vda, vdb, vdc]^T.
  for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
  for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
  for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];

  // Get the other edge vectors.
  for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i];
  for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i];
  for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[i];

  if (!lu_decmp(A, 3, indx, &D, 0)) {
    // A degenerated tet (vol = 0).
    // Return its barycenter.
    for (i = 0; i < 3; i++) {
      ccent[i] = 0.25 * (pa[i] + pb[i] + pc[i] + pd[i]);
    }
    return 1;
  }

  // Check volume if '-a#' and '-a' options are used.
  if (b->varvolume || b->fixedvolume) {
    vol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
    if (b->fixedvolume) {
      if (vol > b->maxvolume) {
        qflag = 1;
      }
    } 
    if (!qflag && b->varvolume) {
      volbnd = volumebound(chktet->tet);
      if ((volbnd > 0.0) && (vol > volbnd)) {
        qflag = 1;
      }
    }
    if (qflag == 1) {
      // Calculate the circumcenter of this tet.
      rhs[0] = 0.5 * dot(vda, vda);
      rhs[1] = 0.5 * dot(vdb, vdb);
      rhs[2] = 0.5 * dot(vdc, vdc);
      lu_solve(A, 3, indx, rhs, 0);            
      for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i];
      return 1;
    }
  }

  if (in->tetunsuitable != nullptr) {
    // Execute the user-defined meshing sizing evaluation.
    if ((*(in->tetunsuitable))(pa, pb, pc, pd, nullptr, 0)) {
      // Calculate the circumcenter of this tet.
      rhs[0] = 0.5 * dot(vda, vda);
      rhs[1] = 0.5 * dot(vdb, vdb);
      rhs[2] = 0.5 * dot(vdc, vdc);
      lu_solve(A, 3, indx, rhs, 0);            
      for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i];
      return 1;
    } else {
      return 0; // Do not split this tet.
    }
  }

  // Check the radius-edge ratio. Set by -q#.
  if (b->minratio > 0) { 
    // Calculate the circumcenter and radius of this tet.
    rhs[0] = 0.5 * dot(vda, vda);
    rhs[1] = 0.5 * dot(vdb, vdb);
    rhs[2] = 0.5 * dot(vdc, vdc);
    lu_solve(A, 3, indx, rhs, 0);            
    for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i];
    rd = sqrt(dot(rhs, rhs));
    // Calculate the shortest edge length.
    elen[0] = dot(vda, vda);
    elen[1] = dot(vdb, vdb);
    elen[2] = dot(vdc, vdc);
    elen[3] = dot(vab, vab);
    elen[4] = dot(vbc, vbc);
    elen[5] = dot(vca, vca);
    smlen = elen[0]; //sidx = 0;
    for (i = 1; i < 6; i++) {
      if (smlen > elen[i]) { 
        smlen = elen[i]; //sidx = i; 
      }
    }
    smlen = sqrt(smlen);
    D = rd / smlen;
    if (D > b->minratio) {
      // A bad radius-edge ratio.
      return 1;
    }
  }

  // Check the minimum dihedral angle. Set by -qq#.
  if (b->mindihedral > 0) { 
    // Compute the 4 face normals (N[0], ..., N[3]).
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) N[j][i] = 0.0;
      N[j][j] = 1.0;  // Positive means the inside direction
      lu_solve(A, 3, indx, N[j], 0);
    }
    for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
    // Normalize the normals.
    for (i = 0; i < 4; i++) {
      L[i] = sqrt(dot(N[i], N[i]));
      assert(L[i] > 0);
      //if (L[i] > 0.0) {
        for (j = 0; j < 3; j++) N[i][j] /= L[i];
      //}
    }
    // Calculate the six dihedral angles.
    cosd[0] = -dot(N[0], N[1]); // Edge cd, bd, bc.
    cosd[1] = -dot(N[0], N[2]);
    cosd[2] = -dot(N[0], N[3]);
    cosd[3] = -dot(N[1], N[2]); // Edge ad, ac
    cosd[4] = -dot(N[1], N[3]);
    cosd[5] = -dot(N[2], N[3]); // Edge ab
    // Get the smallest diehedral angle.
    //maxcosd = mincosd = cosd[0];
    maxcosd = cosd[0];
    for (i = 1; i < 6; i++) {
      //if (cosd[i] > maxcosd) maxcosd = cosd[i];
      maxcosd = (cosd[i] > maxcosd ? cosd[i] : maxcosd);
      //mincosd = (cosd[i] < mincosd ? cosd[i] : maxcosd);
    }
    if (maxcosd > cosmindihed) {
      // Calculate the circumcenter of this tet.
      // A bad dihedral angle.
      //if ((b->quality & 1) == 0) {
        rhs[0] = 0.5 * dot(vda, vda);
        rhs[1] = 0.5 * dot(vdb, vdb);
        rhs[2] = 0.5 * dot(vdc, vdc);
        lu_solve(A, 3, indx, rhs, 0);            
        for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i];
        //*rd = sqrt(dot(rhs, rhs));
      //}
      return 1;
    }
  }

  if (b->metric) { // -m option. Check mesh size. 
    // Calculate the circumradius of this tet.
    rhs[0] = 0.5 * dot(vda, vda);
    rhs[1] = 0.5 * dot(vdb, vdb);
    rhs[2] = 0.5 * dot(vdc, vdc);
    lu_solve(A, 3, indx, rhs, 0);            
    for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i];
    rd = sqrt(dot(rhs, rhs));
    // Check if the ccent lies outside one of the prot.balls at vertices.
    ppt = (point *) &(chktet->tet[4]);
    for (i = 0; i < 4; i++) {
      if (ppt[i][pointmtrindex] > 0) {
        if (rd > ppt[i][pointmtrindex]) {
          qflag = 1; // Enforce mesh size.
          return 1;
        }
      }
    }
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetrahedron()    Split a tetrahedron.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, 
                                 int chkencflag)
{
  triface searchtet;
  face *paryseg;
  point newpt, *ppt;
  badface *bface;
  insertvertexflags ivf;
  int splitflag;
  int i;


  if (qflag == 0) {
    // It is a bad quality tet (not due to mesh size).
    // It can be split if 'ccent' does not encroach upon any prot. balls.
    //   Do a quick check if the 'ccent' lies inside the protect balls
    //   of one of the vertices of this tet.
    ppt = (point *) &(splittet->tet[4]);
    REAL rd = distance(ccent, ppt[0]);
    if ((rd <= ppt[0][pointmtrindex]) || (rd <= ppt[1][pointmtrindex]) ||
        (rd <= ppt[2][pointmtrindex]) || (rd <= ppt[3][pointmtrindex])) {
      return 0;
    }
  }

  makepoint(&newpt, FREEVOLVERTEX);
  for (i = 0; i < 3; i++) newpt[i] = ccent[i];

  searchtet = *splittet;
  ivf.iloc = (int) OUTSIDE;
  // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces;
  ivf.bowywat = 3;
  ivf.lawson = 2;
  ivf.rejflag = 3;  // Do check for encroached segments and subfaces.
  if (qflag == 0) {
    ivf.rejflag |= 4; // Reject it if it lies in some protecting balls.
  }
  ivf.chkencflag = chkencflag;
  ivf.sloc = ivf.sbowywat = 0; // No use.
  ivf.splitbdflag = 0; // No use.
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = 1;

  ivf.refineflag = 1;
  ivf.refinetet = *splittet;


  if (insertpoint(newpt, &searchtet, nullptr, nullptr, &ivf)) {
    // Vertex is inserted.
    st_volref_count++;
    if (steinerleft > 0) steinerleft--;
    if (flipstack != nullptr) {
      flipconstraints fc;
      fc.chkencflag = chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      unflipqueue->restart();
    }
    return 1;
  } else {
    // Point is not inserted.
    pointdealloc(newpt);
    // Check if there are encroached segments/subfaces.
    if (ivf.iloc == (int) ENCSEGMENT) {
      splitflag = 0;
      //if (!b->nobisect) { // not -Y option
      if (!b->nobisect || checkconstraints) {  
        // Select an encroached segment and split it.
        for (i = 0; i < encseglist->objects; i++) {
          paryseg = (face *) fastlookup(encseglist, i);
          if (splitsegment(paryseg, nullptr, qflag, chkencflag | 3)) {
            splitflag = 1; // A point is inserted on a segment.
            break;
          }
        }
      } // if (!b->nobisect)
      encseglist->restart();
      if (splitflag) {
        // Some segments may need to be repaired.
        repairencsegs(chkencflag | 3);
        // Some subfaces may need to be repaired.
        repairencfacs(chkencflag | 2);
        // Queue the tet if it is still alive and not queued.
        if ((splittet->tet != nullptr) && (splittet->tet[4] != nullptr)) {
          enqueuetetrahedron(splittet);
        }
      }
      return splitflag;
    } else if (ivf.iloc == (int) ENCSUBFACE) {
      splitflag = 0;
      //if (!b->nobisect) { // not -Y option
      if (!b->nobisect || checkconstraints) {
        // Select an encroached subface and split it.
        for (i = 0; i < encshlist->objects; i++) {
          bface = (badface *) fastlookup(encshlist, i);
          if (splitsubface(&(bface->ss),nullptr,qflag,bface->cent,chkencflag|2)){
            splitflag = 1; // A point is inserted on a subface or a segment.
            break;
          }
        }
      } // if (!b->nobisect)
      encshlist->restart();
      if (splitflag) {
        assert(badsubsegs->items == 0l);
        // Some subfaces may need to be repaired.
        repairencfacs(chkencflag | 2);
        // Queue the tet if it is still alive.
        if ((splittet->tet != nullptr) && (splittet->tet[4] != nullptr)) {
          enqueuetetrahedron(splittet);
        }
      }
      return splitflag;
    }
    return 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairbadtets()    Repair bad quality tetrahedra.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairbadtets(int chkencflag)
{
  triface *bface;
  REAL ccent[3];
  int qflag = 0;

  int attrnum = numelemattrib - 1;

  // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badtetrahedrons->items > 0) && (steinerleft != 0)) {
    badtetrahedrons->traversalinit();
    bface = (triface *) badtetrahedrons->traverse();
    while ((bface != nullptr) && (steinerleft != 0)) {
      // Skip a deleted element.
      if (bface->ver >= 0) {
        // A queued tet may have been deleted.
        if (!isdeadtet(*bface)) {
          // A queued tet may have been processed.
          if (marktest2ed(*bface)) {
            unmarktest2(*bface);
            if (b->convex) { // -c
              // Skip this tet if it lies in the exterior.
              if (elemattribute(bface->tet, attrnum) != -1.0) {
                if (checktet4split(bface, qflag, ccent)) {
                  splittetrahedron(bface, qflag, ccent, chkencflag);
                }
              }
            } else {
              if (checktet4split(bface, qflag, ccent)) {
                splittetrahedron(bface, qflag, ccent, chkencflag);
              }
            }
          }
        }
        bface->ver = -1; // Signal it as a deleted element.
        badtetrahedrons->dealloc((void *) bface);
      }
      bface = (triface *) badtetrahedrons->traverse();
    }
  }

  if (badtetrahedrons->items > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else {
      assert(0); // Unknown case.
    }
    // Unmark all queued tet.
    badtetrahedrons->traversalinit();
    bface = (triface *) badtetrahedrons->traverse();
    while (bface != nullptr) {
      // Skip a deleted element.
      if (bface->ver >= 0) {
        if (!isdeadtet(*bface)) {
          if (marktest2ed(*bface)) {
            unmarktest2(*bface);
          }
        }
      }
      bface = (triface *) badtetrahedrons->traverse();
    }
    // Clear the pool.
    badtetrahedrons->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enforcequality()    Refine the mesh.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunayrefinement()
{
  triface checktet;
  face checksh;
  face checkseg;
  long steinercount;
  int chkencflag;

  long bak_segref_count, bak_facref_count, bak_volref_count;
  long bak_flipcount = flip23count + flip32count + flip44count;

  if (!b->quiet) {
    printf("Refining mesh...\n");
  }

  if (b->verbose) {
    printf("  Min radiu-edge ratio = %g.\n", b->minratio);
    printf("  Min dihedral   angle = %g.\n", b->mindihedral);
    //printf("  Min Edge length = %g.\n", b->minedgelength);
  }

  steinerleft = b->steinerleft;  // Upperbound of # Steiner points (by -S#).
  if (steinerleft > 0) {
    // Check if we've already used up the given number of Steiner points.
    steinercount = st_segref_count + st_facref_count + st_volref_count;
    if (steinercount < steinerleft) {
      steinerleft -= steinercount;
    } else {
      if (!b->quiet) {
        printf("\nWarning:  ");
        printf("The desired number of Steiner points (%d) has reached.\n\n",
               b->steinerleft);
      }
      return; // No more Steiner points.
    }
  }

  if (b->refine || b->nobisect) { // '-r' or '-Y' option.
    markacutevertices();
  }

  marksharpsegments();

  decidefeaturepointsizes();

  encseglist = new arraypool(sizeof(face), 8);
  encshlist = new arraypool(sizeof(badface), 8);


  //if (!b->nobisect) { // if no '-Y' option
  if (!b->nobisect || checkconstraints) {
    if (b->verbose) {
      printf("  Splitting encroached subsegments.\n");
    }

    chkencflag = 1; // Only check encroaching subsegments.
    steinercount = points->items;

    // Initialize the pool of encroached subsegments.
    badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, 
                                sizeof(void *), 0);

    // Add all segments into the pool.
    subsegs->traversalinit();
    checkseg.sh = shellfacetraverse(subsegs);
    while (checkseg.sh != (shellface *) nullptr) {
      enqueuesubface(badsubsegs, &checkseg);
      checkseg.sh = shellfacetraverse(subsegs);
    }

    // Split all encroached segments.
    repairencsegs(chkencflag);

    if (b->verbose) {
      printf("  Added %ld Steiner points.\n", points->items - steinercount);
    }


    if (b->reflevel > 1) { // '-D2' option
      if (b->verbose) {
        printf("  Splitting encroached subfaces.\n");
      }

      chkencflag = 2; // Only check encroaching subfaces.
      steinercount = points->items;
      bak_segref_count = st_segref_count;
      bak_facref_count = st_facref_count;

      // Initialize the pool of encroached subfaces.
      badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, 
                                  sizeof(void *), 0);

      // Add all subfaces into the pool.
      subfaces->traversalinit();
      checksh.sh = shellfacetraverse(subfaces);
      while (checksh.sh != (shellface *) nullptr) {
        enqueuesubface(badsubfacs, &checksh);
        checksh.sh = shellfacetraverse(subfaces);
      }

      // Split all encroached subfaces.
      repairencfacs(chkencflag);

      if (b->verbose) {
        printf("  Added %ld (%ld,%ld) Steiner points.\n",  
               points->items-steinercount, st_segref_count-bak_segref_count,
               st_facref_count-bak_facref_count);
      }

    } // if (b->reflevel > 1)
  } // if (!b->nobisect)

  if (b->reflevel > 2) { // '-D3' option (The default option)
    if (b->verbose) {
      printf("  Splitting bad quality tets.\n");
    }

    chkencflag = 4; // Only check tetrahedra.
    steinercount = points->items;
    bak_segref_count = st_segref_count;
    bak_facref_count = st_facref_count;
    bak_volref_count = st_volref_count;

    // The cosine value of the min dihedral angle (-qq) for tetrahedra.
    cosmindihed = cos(b->mindihedral / 180.0 * PI);

    // Initialize the pool of bad quality tetrahedra.
    badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock,
                                     sizeof(void *), 0);
    // Add all tetrahedra (no hull tets) into the pool.
    tetrahedrons->traversalinit();
    checktet.tet = tetrahedrontraverse();
    while (checktet.tet != nullptr) {
      enqueuetetrahedron(&checktet);
      checktet.tet = tetrahedrontraverse();
    }

    // Split all bad quality tetrahedra.
    repairbadtets(chkencflag);

    if (b->verbose) {
      printf("  Added %ld (%ld,%ld,%ld) Steiner points.\n", 
             points->items - steinercount, 
             st_segref_count - bak_segref_count,
             st_facref_count - bak_facref_count,
             st_volref_count - bak_volref_count);
    }
  } // if (b->reflevel > 2)

  if (b->verbose) {
    if (flip23count + flip32count + flip44count > bak_flipcount) {
      printf("  Performed %ld flips.\n", flip23count + flip32count +
             flip44count - bak_flipcount);
    }
  }

  if (steinerleft == 0) {
    if (!b->quiet) {
      printf("\nWarnning:  ");
      printf("The desired number of Steiner points (%d) is reached.\n\n",
             b->steinerleft);
    }
  }


  delete encseglist;
  delete encshlist;

  //if (!b->nobisect) {
  if (!b->nobisect || checkconstraints) {
    totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes);
    delete badsubsegs;
    if (b->reflevel > 1) {
      totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes);
      delete badsubfacs;
    }
  }
  if (b->reflevel > 2) {
    totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes);
    delete badtetrahedrons;
  }
}

////                                                                       ////
////                                                                       ////
//// refine_cxx ///////////////////////////////////////////////////////////////

//// optimize_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lawsonflip3d()    A three-dimensional Lawson's algorithm.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::lawsonflip3d(flipconstraints *fc)
{
  triface fliptets[5], neightet, hulltet;
  face checksh, casingout;
  badface *popface, *bface;
  point pd, pe, *pts;
  REAL sign, ori;
  long flipcount, totalcount = 0l;
  long sliver_peels = 0l;
  int t1ver;
  int i;


  while (1) {

    if (b->verbose > 2) {
      printf("      Lawson flip %ld faces.\n", flippool->items);
    }
    flipcount = 0l;

    while (flipstack != (badface *) nullptr) {
      // Pop a face from the stack.
      popface = flipstack;
      fliptets[0] = popface->tt;
      flipstack = flipstack->nextitem; // The next top item in stack.
      flippool->dealloc((void *) popface);

      // Skip it if it is a dead tet (destroyed by previous flips).
      if (isdeadtet(fliptets[0])) continue;
      // Skip it if it is not the same tet as we saved.
      if (!facemarked(fliptets[0])) continue;

      unmarkface(fliptets[0]);

      if (ishulltet(fliptets[0])) continue;

      fsym(fliptets[0], fliptets[1]);
      if (ishulltet(fliptets[1])) {
        if (nonconvex) {
          // Check if 'fliptets[0]' it is a hull sliver.
          tspivot(fliptets[0], checksh);
          for (i = 0; i < 3; i++) {
            if (!isshsubseg(checksh)) {
              spivot(checksh, casingout);
              //assert(casingout.sh != nullptr);
              if (sorg(checksh) != sdest(casingout)) sesymself(casingout);
              stpivot(casingout, neightet);
              if (neightet.tet == fliptets[0].tet) {
                // Found a hull sliver 'neightet'. Let it be [e,d,a,b], where 
                //   [e,d,a] and [d,e,b] are hull faces.
                edestoppo(neightet, hulltet); // [a,b,e,d]
                fsymself(hulltet); // [b,a,e,#]
                if (oppo(hulltet) == dummypoint) {
                  pe = org(neightet);
                  if ((pointtype(pe) == FREEFACETVERTEX) ||
                      (pointtype(pe) == FREESEGVERTEX)) {
                    removevertexbyflips(pe);
                  }
                } else {
                  eorgoppo(neightet, hulltet); // [b,a,d,e]
                  fsymself(hulltet); // [a,b,d,#]
                  if (oppo(hulltet) == dummypoint) {
                    pd = dest(neightet);
                    if ((pointtype(pd) == FREEFACETVERTEX) ||
                        (pointtype(pd) == FREESEGVERTEX)) {
                      removevertexbyflips(pd);
                    }
                  } else {
                    // Perform a 3-to-2 flip to remove the sliver.
                    fliptets[0] = neightet;          // [e,d,a,b]
                    fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
                    fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
                    flip32(fliptets, 1, fc);
                    // Update counters.
                    flip32count--;
                    flip22count--;
                    sliver_peels++;
                    if (fc->remove_ndelaunay_edge) {
                      // Update the volume (must be decreased).
                      //assert(fc->tetprism_vol_sum <= 0);
                      tetprism_vol_sum += fc->tetprism_vol_sum;
                      fc->tetprism_vol_sum = 0.0; // Clear it.
                    }
                  }
                }
                break;
              } // if (neightet.tet == fliptets[0].tet)
            } // if (!isshsubseg(checksh))
            senextself(checksh);
          } // i
        } // if (nonconvex)
        continue;
      }

      if (checksubfaceflag) {
        // Do not flip if it is a subface.
        if (issubface(fliptets[0])) continue;
      }

      // Test whether the face is locally Delaunay or not.
      pts = (point *) fliptets[1].tet; 
      sign = insphere_s(pts[4], pts[5], pts[6], pts[7], oppo(fliptets[0]));

      if (sign < 0) {
        // A non-Delaunay face. Try to flip it.
        pd = oppo(fliptets[0]);
        pe = oppo(fliptets[1]);

        // Check the convexity of its three edges. Stop checking either a
        //   locally non-convex edge (ori < 0) or a flat edge (ori = 0) is
        //   encountered, and 'fliptet' represents that edge.
        for (i = 0; i < 3; i++) {
          ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe);
          if (ori <= 0) break;
          enextself(fliptets[0]);
        }

        if (ori > 0) {
          // A 2-to-3 flip is found.
          //   [0] [a,b,c,d], 
          //   [1] [b,a,c,e]. no dummypoint.
          flip23(fliptets, 0, fc);
          flipcount++;
          if (fc->remove_ndelaunay_edge) {
            // Update the volume (must be decreased).
            //assert(fc->tetprism_vol_sum <= 0);
            tetprism_vol_sum += fc->tetprism_vol_sum;
            fc->tetprism_vol_sum = 0.0; // Clear it.
          }
          continue;
        } else { // ori <= 0
          // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat,
          //   where the edge [a',b'] is one of [a,b], [b,c], and [c,a].
          if (checksubsegflag) {
            // Do not flip if it is a segment.
            if (issubseg(fliptets[0])) continue;
          }
          // Check if there are three or four tets sharing at this edge.        
          esymself(fliptets[0]); // [b,a,d,c]
          for (i = 0; i < 3; i++) {
            fnext(fliptets[i], fliptets[i+1]);
          }
          if (fliptets[3].tet == fliptets[0].tet) {
            // A 3-to-2 flip is found. (No hull tet.)
            flip32(fliptets, 0, fc); 
            flipcount++;
            if (fc->remove_ndelaunay_edge) {
              // Update the volume (must be decreased).
              //assert(fc->tetprism_vol_sum <= 0);
              tetprism_vol_sum += fc->tetprism_vol_sum;
              fc->tetprism_vol_sum = 0.0; // Clear it.
            }
            continue;
          } else {
            // There are more than 3 tets at this edge.
            fnext(fliptets[3], fliptets[4]);
            if (fliptets[4].tet == fliptets[0].tet) {
              // There are exactly 4 tets at this edge.
              if (nonconvex) {
                if (apex(fliptets[3]) == dummypoint) {
                  // This edge is locally non-convex on the hull.
                  // It can be removed by a 4-to-4 flip.                  
                  ori = 0;
                }
              } // if (nonconvex)
              if (ori == 0) {
                // A 4-to-4 flip is found. (Two hull tets may be involved.)
                // Current tets in 'fliptets':
                //   [0] [b,a,d,c] (d may be newpt)
                //   [1] [b,a,c,e]
                //   [2] [b,a,e,f] (f may be dummypoint)
                //   [3] [b,a,f,d]
                esymself(fliptets[0]); // [a,b,c,d] 
                // A 2-to-3 flip replaces face [a,b,c] by edge [e,d].
                //   This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]).
                //   It will be removed by the followed 3-to-2 flip.
                flip23(fliptets, 0, fc); // No hull tet.
                fnext(fliptets[3], fliptets[1]);
                fnext(fliptets[1], fliptets[2]);
                // Current tets in 'fliptets':
                //   [0] [...]
                //   [1] [b,a,d,e] (degenerated, d may be new point).
                //   [2] [b,a,e,f] (f may be dummypoint)
                //   [3] [b,a,f,d]
                // A 3-to-2 flip replaces edge [b,a] by face [d,e,f].
                //   Hull tets may be involved (f may be dummypoint).
                flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc);
                flipcount++;
                flip23count--;
                flip32count--;
                flip44count++;
                if (fc->remove_ndelaunay_edge) {
                  // Update the volume (must be decreased).
                  //assert(fc->tetprism_vol_sum <= 0);
                  tetprism_vol_sum += fc->tetprism_vol_sum;
                  fc->tetprism_vol_sum = 0.0; // Clear it.
                }
                continue;
              } // if (ori == 0)
            }
          }
        } // if (ori <= 0)

        // This non-Delaunay face is unflippable. Save it.
        unflipqueue->newindex((void **) &bface);
        bface->tt = fliptets[0];
        bface->forg  = org(fliptets[0]);
        bface->fdest = dest(fliptets[0]);
        bface->fapex = apex(fliptets[0]);
      } // if (sign < 0)
    } // while (flipstack)

    if (b->verbose > 2) {
      if (flipcount > 0) {
        printf("      Performed %ld flips.\n", flipcount);
      }
    }
    // Accumulate the counter of flips.
    totalcount += flipcount;

    assert(flippool->items == 0l);
    // Return if no unflippable faces left.
    if (unflipqueue->objects == 0l) break; 
    // Return if no flip has been performed.
    if (flipcount == 0l) break;

    // Try to flip the unflippable faces.
    for (i = 0; i < unflipqueue->objects; i++) {
      bface = (badface *) fastlookup(unflipqueue, i);
      if (!isdeadtet(bface->tt) && 
          (org(bface->tt) == bface->forg) &&
          (dest(bface->tt) == bface->fdest) &&
          (apex(bface->tt) == bface->fapex)) {
        flippush(flipstack, &(bface->tt));
      }
    }
    unflipqueue->restart();

  } // while (1)

  if (b->verbose > 2) {
    if (totalcount > 0) {
      printf("      Performed %ld flips.\n", totalcount);
    }
    if (sliver_peels > 0) {
      printf("      Removed %ld hull slivers.\n", sliver_peels);
    }
    if (unflipqueue->objects > 0l) {
      printf("      %ld unflippable edges remained.\n", unflipqueue->objects);
    }
  }

  return totalcount + sliver_peels;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoverdelaunay()    Recovery the locally Delaunay property.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::recoverdelaunay()
{
  arraypool *flipqueue, *nextflipqueue, *swapqueue;
  triface tetloop, neightet, *parytet;
  badface *bface, *parybface;
  point *ppt;
  flipconstraints fc;
  int i, j;

  if (!b->quiet) {
    printf("Recovering Delaunayness...\n");
  }

  tetprism_vol_sum = 0.0; // Initialize it.

  // Put all interior faces of the mesh into 'flipstack'.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != nullptr) {
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      decode(tetloop.tet[tetloop.ver], neightet);
      if (!facemarked(neightet)) {
        flippush(flipstack, &tetloop);
      }
    }
    ppt = (point *) &(tetloop.tet[4]);
    tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]);
    tetloop.tet = tetrahedrontraverse();
  }

  // Calulate a relatively lower bound for small improvement. 
  //   Used to avoid rounding error in volume calculation.
  fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3;

  if (b->verbose) {
    printf("  Initial obj = %.17g\n", tetprism_vol_sum);
  }

  if (b->verbose > 1) {
    printf("    Recover Delaunay [Lawson] : %ld\n", flippool->items);
  }

  // First only use the basic Lawson's flip.
  fc.remove_ndelaunay_edge = 1;
  fc.enqflag = 2;

  lawsonflip3d(&fc);

  if (b->verbose > 1) {
    printf("    obj (after Lawson) = %.17g\n", tetprism_vol_sum);
  }

  if (unflipqueue->objects == 0l) {
    return; // The mesh is Delaunay.
  }

  fc.unflip = 1; // Unflip if the edge is not flipped.
  fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'.
  fc.enqflag = 0;

  autofliplinklevel = 1; // Init level.
  b->fliplinklevel = -1; // No fixed level.

  // For efficiency reason, we limit the maximium size of the edge star.
  int bakmaxflipstarsize = b->flipstarsize;
  b->flipstarsize = 10; // default

  flipqueue = new arraypool(sizeof(badface), 10);
  nextflipqueue = new arraypool(sizeof(badface), 10);
  
  // Swap the two flip queues.
  swapqueue = flipqueue;
  flipqueue = unflipqueue;
  unflipqueue = swapqueue;

  while (flipqueue->objects > 0l) {

    if (b->verbose > 1) {
      printf("    Recover Delaunay [level = %2d] #:  %ld.\n",
             autofliplinklevel, flipqueue->objects);
    }

    for (i = 0; i < flipqueue->objects; i++) {
      bface  = (badface *) fastlookup(flipqueue, i);
      if (getedge(bface->forg, bface->fdest, &bface->tt)) {
        if (removeedgebyflips(&(bface->tt), &fc) == 2) {
          tetprism_vol_sum += fc.tetprism_vol_sum;
          fc.tetprism_vol_sum = 0.0; // Clear it.
          // Queue new faces for flips.
          for (j = 0; j < cavetetlist->objects; j++) {
            parytet = (triface *) fastlookup(cavetetlist, j);
            // A queued new tet may be dead.
            if (!isdeadtet(*parytet)) {
              for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
                // Avoid queue a face twice.
                decode(parytet->tet[parytet->ver], neightet);
                if (!facemarked(neightet)) {
                  flippush(flipstack, parytet);
                }
              } // parytet->ver
            }
          } // j
          cavetetlist->restart();
          // Remove locally non-Delaunay faces. New non-Delaunay edges
          //   may be found. They are saved in 'unflipqueue'.
          fc.enqflag = 2;
          lawsonflip3d(&fc);
          fc.enqflag = 0;
          // There may be unflipable faces. Add them in flipqueue.
          for (j = 0; j < unflipqueue->objects; j++) {
            bface  = (badface *) fastlookup(unflipqueue, j);
            flipqueue->newindex((void **) &parybface);
            *parybface = *bface;
          }
          unflipqueue->restart();
        } else {
          // Unable to remove this edge. Save it.
          nextflipqueue->newindex((void **) &parybface);
          *parybface = *bface;
          // Normally, it should be zero. 
          //assert(fc.tetprism_vol_sum == 0.0);
          // However, due to rounding errors, a tiny value may appear.
          fc.tetprism_vol_sum = 0.0;
        }
      }
    } // i

    if (b->verbose > 1) {
      printf("    obj (after level %d) = %.17g.\n", autofliplinklevel,
             tetprism_vol_sum);
    }
    flipqueue->restart();

    // Swap the two flip queues.
    swapqueue = flipqueue;
    flipqueue = nextflipqueue;
    nextflipqueue = swapqueue;

    if (flipqueue->objects > 0l) {
      // default 'b->delmaxfliplevel' is 1.
      if (autofliplinklevel >= b->delmaxfliplevel) {
        // For efficiency reason, we do not search too far.
        break;
      }
      autofliplinklevel+=b->fliplinklevelinc;
    }
  } // while (flipqueue->objects > 0l)

  if (flipqueue->objects > 0l) {
    if (b->verbose > 1) {
      printf("    %ld non-Delaunay edges remained.\n", flipqueue->objects);
    }
  }

  if (b->verbose) {
    printf("  Final obj  = %.17g\n", tetprism_vol_sum);
  }

  b->flipstarsize = bakmaxflipstarsize;
  delete flipqueue;
  delete nextflipqueue;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// gettetrahedron()    Get a tetrahedron which have the given vertices.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, 
                               triface *searchtet)
{
  triface spintet;
  int t1ver; 

  if (getedge(pa, pb, searchtet)) {
    spintet = *searchtet;
    while (1) {
      if (apex(spintet) == pc) {
        *searchtet = spintet;
        break;
      }
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    }
    if (apex(*searchtet) == pc) {
      if (oppo(*searchtet) == pd) {
        return 1;
      } else {
        fsymself(*searchtet);
        if (oppo(*searchtet) == pd) {
          return 1;
        }
      }
    }
  }

  return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// improvequalitybyflips()    Improve the mesh quality by flips.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::improvequalitybyflips()
{
  arraypool *flipqueue, *nextflipqueue, *swapqueue;
  badface *bface, *parybface;
  triface *parytet;
  point *ppt;
  flipconstraints fc;
  REAL *cosdd, ncosdd[6], maxdd;
  long totalremcount, remcount;
  int remflag;
  int n, i, j, k;

  //assert(unflipqueue->objects > 0l);
  flipqueue = new arraypool(sizeof(badface), 10);
  nextflipqueue = new arraypool(sizeof(badface), 10);

  // Backup flip edge options.
  int bakautofliplinklevel = autofliplinklevel;
  int bakfliplinklevel = b->fliplinklevel;
  int bakmaxflipstarsize = b->flipstarsize;

  // Set flip edge options.
  autofliplinklevel = 1; 
  b->fliplinklevel = -1;
  b->flipstarsize = 10; // b->optmaxflipstarsize;

  fc.remove_large_angle = 1;
  fc.unflip = 1;
  fc.collectnewtets = 1;
  fc.checkflipeligibility = 1;

  totalremcount = 0l;

  // Swap the two flip queues.
  swapqueue = flipqueue;
  flipqueue = unflipqueue;
  unflipqueue = swapqueue;

  while (flipqueue->objects > 0l) {

    remcount = 0l;

    while (flipqueue->objects > 0l) {
      if (b->verbose > 1) {
        printf("    Improving mesh qualiy by flips [%d]#:  %ld.\n",
               autofliplinklevel, flipqueue->objects);
      }

      for (k = 0; k < flipqueue->objects; k++) {
        bface  = (badface *) fastlookup(flipqueue, k);
        if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
                           bface->foppo, &bface->tt)) {
          //assert(!ishulltet(bface->tt));
          // There are bad dihedral angles in this tet.
          if (bface->tt.ver != 11) {
            // The dihedral angles are permuted.
            // Here we simply re-compute them. Slow!!.
            ppt = (point *) & (bface->tt.tet[4]);
            tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
                           &bface->key, nullptr);
            bface->forg = ppt[0];
            bface->fdest = ppt[1];
            bface->fapex = ppt[2];
            bface->foppo = ppt[3];
            bface->tt.ver = 11;
          }
          if (bface->key == 0) {
            // Re-comput the quality values. Due to smoothing operations.
            ppt = (point *) & (bface->tt.tet[4]);
            tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
                           &bface->key, nullptr);
          }
          cosdd = bface->cent;
          remflag = 0;
          for (i = 0; (i < 6) && !remflag; i++) {
            if (cosdd[i] < cosmaxdihed) {
              // Found a large dihedral angle.
              bface->tt.ver = edge2ver[i]; // Go to the edge.
              fc.cosdihed_in = cosdd[i];
              fc.cosdihed_out = 0.0; // 90 degree.
              n = removeedgebyflips(&(bface->tt), &fc);
              if (n == 2) {
                // Edge is flipped.
                remflag = 1;
                if (fc.cosdihed_out < cosmaxdihed) {
                  // Queue new bad tets for further improvements.
                  for (j = 0; j < cavetetlist->objects; j++) {
                    parytet = (triface *) fastlookup(cavetetlist, j);
                    if (!isdeadtet(*parytet)) {
                      ppt = (point *) & (parytet->tet[4]);
                      // Do not test a hull tet.
                      if (ppt[3] != dummypoint) {
                        tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, 
                                       &maxdd, nullptr);
                        if (maxdd < cosmaxdihed) {
                          // There are bad dihedral angles in this tet.
                          nextflipqueue->newindex((void **) &parybface); 
                          parybface->tt.tet = parytet->tet;
                          parybface->tt.ver = 11;
                          parybface->forg = ppt[0];
                          parybface->fdest = ppt[1];
                          parybface->fapex = ppt[2];
                          parybface->foppo = ppt[3];
                          parybface->key = maxdd;
                          for (n = 0; n < 6; n++) {
                            parybface->cent[n] = ncosdd[n];
                          }
                        }
                      } // if (ppt[3] != dummypoint) 
                    }
                  } // j
                } // if (fc.cosdihed_out < cosmaxdihed)
                cavetetlist->restart();
                remcount++;
              }
            }
          } // i          
          if (!remflag) {
            // An unremoved bad tet. Queue it again. 
            unflipqueue->newindex((void **) &parybface);
            *parybface = *bface;
          }
        } // if (gettetrahedron(...))
      } // k

      flipqueue->restart();

      // Swap the two flip queues.
      swapqueue = flipqueue;
      flipqueue = nextflipqueue;
      nextflipqueue = swapqueue;
    } // while (flipqueues->objects > 0)

    if (b->verbose > 1) {
      printf("    Removed %ld bad tets.\n", remcount);
    }
    totalremcount += remcount;

    if (unflipqueue->objects > 0l) {
      //if (autofliplinklevel >= b->optmaxfliplevel) {
      if (autofliplinklevel >= b->optlevel) {
        break;
      }
      autofliplinklevel+=b->fliplinklevelinc;
      //b->flipstarsize = 10 + (1 << (b->optlevel - 1));
    }

    // Swap the two flip queues.
    swapqueue = flipqueue;
    flipqueue = unflipqueue;
    unflipqueue = swapqueue;
  } // while (flipqueues->objects > 0)

  // Restore original flip edge options.
  autofliplinklevel = bakautofliplinklevel;
  b->fliplinklevel = bakfliplinklevel;
  b->flipstarsize = bakmaxflipstarsize;

  delete flipqueue;
  delete nextflipqueue;

  return totalremcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// smoothpoint()    Moving a vertex to improve the mesh quality.             //
//                                                                           //
// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point.  //
// It may be not a vertex of the mesh.                                       //
//                                                                           //
// This routine tries to move 'p' inside its star until a selected objective //
// function over all tetrahedra in the star is improved. The function may be //
// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or //
// simply the volume of the tetrahedra.                                      //
//                                                                           //
// 'linkfacelist' contains the list of link faces of 'p'.  Since a link face //
// has two orientations, ccw or cw, with respect to 'p'.  'ccw' indicates    //
// the orientation is ccw (1) or not (0).                                    //
//                                                                           //
// 'opm' is a structure contains the parameters of the objective function.   //
// It is needed by the evaluation of the function value.                     //
//                                                                           //
// The return value indicates weather the point is smoothed or not.          //
//                                                                           //
// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, //
// no face has 'dummypoint' as its vertex.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw,
                            optparameters *opm)
{
  triface *parytet, *parytet1, swaptet;
  point pa, pb, pc;
  REAL fcent[3], startpt[3], nextpt[3], bestpt[3];
  REAL oldval, minval = 0.0, val;
  REAL maxcosd; // oldang, newang;
  REAL ori, diff;
  int numdirs, iter;
  int i, j, k;

  // Decide the number of moving directions.
  numdirs = (int) linkfacelist->objects;
  if (numdirs > opm->numofsearchdirs) {
    numdirs = opm->numofsearchdirs; // Maximum search directions.
  }

  // Set the initial value.
  if (!opm->max_min_volume) {
    assert(opm->initval >= 0.0);
  }
  opm->imprval = opm->initval;
  iter = 0;

  for (i = 0; i < 3; i++) {
    bestpt[i] = startpt[i] = smtpt[i];
  }

  // Iterate until the obj function is not improved.
  while (1) {

    // Find the best next location.
    oldval = opm->imprval;

    for (i = 0; i < numdirs; i++) {
      // Randomly pick a link face (0 <= k <= objects - i - 1).
      k = (int) randomnation(linkfacelist->objects - i);
      parytet = (triface *) fastlookup(linkfacelist, k);
      // Calculate a new position from 'p' to the center of this face.
      pa = org(*parytet);
      pb = dest(*parytet);
      pc = apex(*parytet);
      for (j = 0; j < 3; j++) {
        fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0;
      }
      for (j = 0; j < 3; j++) {
        nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); 
      }
      // Calculate the largest minimum function value for the new location.
      for (j = 0; j < linkfacelist->objects; j++) {
        parytet = (triface *) fastlookup(linkfacelist, j);
        if (ccw) {
          pa = org(*parytet);
          pb = dest(*parytet);
        } else {
          pb = org(*parytet);
          pa = dest(*parytet);
        }
        pc = apex(*parytet);
        ori = orient3d(pa, pb, pc, nextpt);
        if (ori < 0.0) {
          // Calcuate the objective function value. 
          if (opm->max_min_volume) {
            //val = -ori;
            val = - orient3dfast(pa, pb, pc, nextpt);
          } else if (opm->max_min_aspectratio) {
            val = tetaspectratio(pa, pb, pc, nextpt);
          } else if (opm->min_max_dihedangle) {
            tetalldihedral(pa, pb, pc, nextpt, nullptr, &maxcosd, nullptr);
            if (maxcosd < -1) maxcosd = -1.0; // Rounding.
            val = maxcosd + 1.0; // Make it be positive. 
          } else {
            // Unknown objective function.
            val = 0.0;
          }  
        } else { // ori >= 0.0;
          // An invalid new tet. 
          // This may happen if the mesh contains inverted elements.
          if (opm->max_min_volume) {
            //val = -ori;
            val = - orient3dfast(pa, pb, pc, nextpt);    
          } else {
            // Discard this point.
            break; // j
          }
        } // if (ori >= 0.0)
        // Stop looping when the object value is not improved.
        if (val <= opm->imprval) {
          break; // j
        } else {
          // Remember the smallest improved value.
          if (j == 0) {
            minval = val;
          } else {
            minval = (val < minval) ? val : minval;
          }
        }
      } // j
      if (j == linkfacelist->objects) {
        // The function value has been improved.
        opm->imprval = minval;
        // Save the new location of the point.
        for (j = 0; j < 3; j++) bestpt[j] = nextpt[j];
      }
      // Swap k-th and (object-i-1)-th entries.
      j = linkfacelist->objects - i - 1;
      parytet  = (triface *) fastlookup(linkfacelist, k);
      parytet1 = (triface *) fastlookup(linkfacelist, j);
      swaptet = *parytet1;
      *parytet1 = *parytet;
      *parytet = swaptet;
    } // i

    diff = opm->imprval - oldval;
    if (diff > 0.0) {
      // Is the function value improved effectively?
      if (opm->max_min_volume) {
        //if ((diff / oldval) < b->epsilon) diff = 0.0;  
      } else if (opm->max_min_aspectratio) {
        if ((diff / oldval) < 1e-3) diff = 0.0;
      } else if (opm->min_max_dihedangle) {
        //oldang = acos(oldval - 1.0);
        //newang = acos(opm->imprval - 1.0);
        //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree.
      } else {
        // Unknown objective function.
        assert(0); // Not possible.
      }
    }

    if (diff > 0.0) {
      // Yes, move p to the new location and continue.
      for (j = 0; j < 3; j++) startpt[j] = bestpt[j];
      iter++;
      if ((opm->maxiter > 0) && (iter >= opm->maxiter)) {
        // Maximum smoothing iterations reached.
        break;
      }
    } else {
      break;
    }

  } // while (1)

  if (iter > 0) {
    // The point has been smooothed.
    opm->smthiter = iter; // Remember the number of iterations. 
    // The point has been smoothed. Update it to its new position.
    for (i = 0; i < 3; i++) smtpt[i] = startpt[i];
  }

  return iter;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// improvequalitysmoothing()    Improve mesh quality by smoothing.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::improvequalitybysmoothing(optparameters *opm)
{
  arraypool *flipqueue, *swapqueue;
  triface *parytet;
  badface *bface, *parybface;
  point *ppt;
  long totalsmtcount, smtcount;
  int smtflag;
  int iter, i, j, k;

  //assert(unflipqueue->objects > 0l);
  flipqueue = new arraypool(sizeof(badface), 10);

  // Swap the two flip queues.
  swapqueue = flipqueue;
  flipqueue = unflipqueue;
  unflipqueue = swapqueue;

  totalsmtcount = 0l;
  iter = 0;

  while (flipqueue->objects > 0l) {

    smtcount = 0l;

    if (b->verbose > 1) {
      printf("    Improving mesh qualiy by smoothing [%d]#:  %ld.\n",
             iter, flipqueue->objects);
    }

    for (k = 0; k < flipqueue->objects; k++) {      
      bface  = (badface *) fastlookup(flipqueue, k);
      if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
                         bface->foppo, &bface->tt)) {
        // Operate on it if it is not in 'unflipqueue'.
        if (!marktested(bface->tt)) {
          // Here we simply re-compute the quality. Since other smoothing
          //   operation may have moved the vertices of this tet.
          ppt = (point *) & (bface->tt.tet[4]);
          tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
                         &bface->key, nullptr);
          if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) {
            // It is a sliver. Try to smooth its vertices.
            smtflag = 0;
            opm->initval = bface->key + 1.0; 
            for (i = 0; (i < 4) && !smtflag; i++) {
              if (pointtype(ppt[i]) == FREEVOLVERTEX) {
                getvertexstar(1, ppt[i], cavetetlist, nullptr, nullptr);
                opm->searchstep = 0.001; // Search step size
                smtflag = smoothpoint(ppt[i], cavetetlist, 1, opm);
                if (smtflag) {
                  while (opm->smthiter == opm->maxiter) {
                    opm->searchstep *= 10.0; // Increase the step size.
                    opm->initval = opm->imprval;
                    opm->smthiter = 0; // reset
                    smoothpoint(ppt[i], cavetetlist, 1, opm);
                  }
                  // This tet is modifed.
                  smtcount++;
                  if ((opm->imprval - 1.0) < cossmtdihed) {
                    // There are slivers in new tets. Queue them.
                    for (j = 0; j < cavetetlist->objects; j++) {
                      parytet = (triface *) fastlookup(cavetetlist, j);
                      assert(!isdeadtet(*parytet));
                      // Operate it if it is not in 'unflipqueue'.
                      if (!marktested(*parytet)) {
                        // Evaluate its quality.
                        // Re-use ppt, bface->key, bface->cent.
                        ppt = (point *) & (parytet->tet[4]);
                        tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], 
                                       bface->cent, &bface->key, nullptr);
                        if (bface->key < cossmtdihed) {
                          // A new sliver. Queue it.
                          marktest(*parytet); // It is in unflipqueue.
                          unflipqueue->newindex((void **) &parybface);
                          parybface->tt = *parytet;
                          parybface->forg = ppt[0];
                          parybface->fdest = ppt[1];
                          parybface->fapex = ppt[2];
                          parybface->foppo = ppt[3];
                          parybface->tt.ver = 11; 
                          parybface->key = 0.0;
                        }
                      }
                    } // j
                  } // if ((opm->imprval - 1.0) < cossmtdihed)
                } // if (smtflag)
                cavetetlist->restart();
              } // if (pointtype(ppt[i]) == FREEVOLVERTEX)
            } // i
            if (!smtflag) {
              // Didn't smooth. Queue it again.
              marktest(bface->tt); // It is in unflipqueue.
              unflipqueue->newindex((void **) &parybface);
              parybface->tt = bface->tt;
              parybface->forg = ppt[0];
              parybface->fdest = ppt[1];
              parybface->fapex = ppt[2];
              parybface->foppo = ppt[3];
              parybface->tt.ver = 11;
              parybface->key = 0.0;
            }
              } // if (maxdd < cosslidihed)
        } // if (!marktested(...))
      } // if (gettetrahedron(...))
    } // k

    flipqueue->restart();

    // Unmark the tets in unflipqueue.
    for (i = 0; i < unflipqueue->objects; i++) {
      bface  = (badface *) fastlookup(unflipqueue, i);
      unmarktest(bface->tt);
    }

    if (b->verbose > 1) {
      printf("    Smooth %ld points.\n", smtcount);
    }
    totalsmtcount += smtcount;

    if (smtcount == 0l) {
      // No point has been smoothed. 
      break;
    } else {
      iter++;
      if (iter == 2) { //if (iter >= b->optpasses) {
        break;
      }
    }

    // Swap the two flip queues.
    swapqueue = flipqueue;
    flipqueue = unflipqueue;
    unflipqueue = swapqueue;
  } // while

  delete flipqueue;

  return totalsmtcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsliver()    Split a sliver.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag)
{
  triface *abtets;
  triface searchtet, spintet, *parytet;
  point pa, pb, steinerpt;
  optparameters opm;
  insertvertexflags ivf;
  REAL smtpt[3], midpt[3];
  int success;
  int t1ver;
  int n, i;

  // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. 
  // Go to the opposite edge [a,b].
  edestoppo(*slitet, searchtet); // [a,b,c,d].

  // Do not split a segment.
  if (issubseg(searchtet)) {
    return 0; 
  }

  // Count the number of tets shared at [a,b].
  // Do not split it if it is a hull edge.
  spintet = searchtet;
  n = 0; 
  while (1) {
    if (ishulltet(spintet)) break;
    n++;
    fnextself(spintet);
    if (spintet.tet == searchtet.tet) break;
  }
  if (ishulltet(spintet)) {
    return 0; // It is a hull edge.
  }
  assert(n >= 3);

  // Get all tets at edge [a,b].
  abtets = new triface[n];
  spintet = searchtet;
  for (i = 0; i < n; i++) {
    abtets[i] = spintet;
    fnextself(spintet);
  }

  // Initialize the list of 2n boundary faces.
  for (i = 0; i < n; i++) {    
    eprev(abtets[i], searchtet);
    esymself(searchtet); // [a,p_i,p_i+1].
    cavetetlist->newindex((void **) &parytet);
    *parytet = searchtet;
    enext(abtets[i], searchtet);
    esymself(searchtet); // [p_i,b,p_i+1].
    cavetetlist->newindex((void **) &parytet);
    *parytet = searchtet;
  }

  // Init the Steiner point at the midpoint of edge [a,b].
  pa = org(abtets[0]);
  pb = dest(abtets[0]);
  for (i = 0; i < 3; i++) {
    smtpt[i] = midpt[i] = 0.5 * (pa[i] + pb[i]);
  }

  // Point smooth options.
  opm.min_max_dihedangle = 1;
  opm.initval = cosd + 1.0; // Initial volume is zero.
  opm.numofsearchdirs = 20;
  opm.searchstep = 0.001;  
  opm.maxiter = 100; // Limit the maximum iterations.

  success = smoothpoint(smtpt, cavetetlist, 1, &opm);

  if (success) {
    while (opm.smthiter == opm.maxiter) {
      // It was relocated and the prescribed maximum iteration reached. 
      // Try to increase the search stepsize.
      opm.searchstep *= 10.0;
      //opm.maxiter = 100; // Limit the maximum iterations.
      opm.initval = opm.imprval;
      opm.smthiter = 0; // Init.
      smoothpoint(smtpt, cavetetlist, 1, &opm);  
    }
  } // if (success)

  cavetetlist->restart();

  if (!success) {
    delete [] abtets;
    return 0;
  }


  // Insert the Steiner point.
  makepoint(&steinerpt, FREEVOLVERTEX);
  for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i];

  // Insert the created Steiner point.
  for (i = 0; i < n; i++) {
    infect(abtets[i]);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = abtets[i];
  }

  searchtet = abtets[0]; // No need point location.
  if (b->metric) {
    locate(steinerpt, &searchtet, 0); // For size interpolation.
  }

  delete [] abtets;

  ivf.iloc = (int) INSTAR;
  ivf.chkencflag = chkencflag;
  ivf.assignmeshsize = b->metric; 


  if (insertpoint(steinerpt, &searchtet, nullptr, nullptr, &ivf)) {
    // The vertex has been inserted.
    st_volref_count++; 
    if (steinerleft > 0) steinerleft--;
    return 1;
  } else {
    // The Steiner point is too close to an existing vertex. Reject it.
    pointdealloc(steinerpt);
    return 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeslivers()    Remove slivers by adding Steiner points.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::removeslivers(int chkencflag)
{
  arraypool *flipqueue, *swapqueue;
  badface *bface, *parybface;
  triface slitet, *parytet;
  point *ppt;
  REAL cosdd[6], maxcosd;
  long totalsptcount, sptcount;
  int iter, i, j, k;

  //assert(unflipqueue->objects > 0l);
  flipqueue = new arraypool(sizeof(badface), 10);

  // Swap the two flip queues.
  swapqueue = flipqueue;
  flipqueue = unflipqueue;
  unflipqueue = swapqueue;

  totalsptcount = 0l;
  iter = 0;

  while ((flipqueue->objects > 0l) && (steinerleft != 0)) {

    sptcount = 0l;

    if (b->verbose > 1) {
      printf("    Splitting bad quality tets [%d]#:  %ld.\n",
             iter, flipqueue->objects);
    }

    for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) {      
      bface  = (badface *) fastlookup(flipqueue, k);
      if (gettetrahedron(bface->forg, bface->fdest, bface->fapex,
                         bface->foppo, &bface->tt)) {
        if ((bface->key == 0) || (bface->tt.ver != 11)) {
          // Here we need to re-compute the quality. Since other smoothing
          //   operation may have moved the vertices of this tet.
          ppt = (point *) & (bface->tt.tet[4]);
          tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, 
                         &bface->key, nullptr);
        }
        if (bface->key < cosslidihed) { 
          // It is a sliver. Try to split it.
          slitet.tet = bface->tt.tet;
          //cosdd = bface->cent;
          for (j = 0; j < 6; j++) {
            if (bface->cent[j] < cosslidihed) { 
              // Found a large dihedral angle.
              slitet.ver = edge2ver[j]; // Go to the edge.
              if (splitsliver(&slitet, bface->cent[j], chkencflag)) {
                sptcount++;
                break;
              }
            }
          } // j
          if (j < 6) {
            // A sliver is split. Queue new slivers.
            badtetrahedrons->traversalinit();
            parytet = (triface *) badtetrahedrons->traverse();
            while (parytet != nullptr) {
              unmarktest2(*parytet);
              ppt = (point *) & (parytet->tet[4]);
              tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, 
                             &maxcosd, nullptr);
              if (maxcosd < cosslidihed) {
                // A new sliver. Queue it.
                unflipqueue->newindex((void **) &parybface);
                parybface->forg = ppt[0];
                parybface->fdest = ppt[1];
                parybface->fapex = ppt[2];
                parybface->foppo = ppt[3];
                parybface->tt.tet = parytet->tet;
                parybface->tt.ver = 11;
                parybface->key = maxcosd;
                for (i = 0; i < 6; i++) {
                  parybface->cent[i] = cosdd[i];
                }
              }
              parytet = (triface *) badtetrahedrons->traverse();
            }
            badtetrahedrons->restart();
          } else {
            // Didn't split. Queue it again.
            unflipqueue->newindex((void **) &parybface);
            *parybface = *bface;
          } // if (j == 6)
        } // if (bface->key < cosslidihed)
      } // if (gettetrahedron(...))
    } // k

    flipqueue->restart();

    if (b->verbose > 1) {
      printf("    Split %ld tets.\n", sptcount);
    }
    totalsptcount += sptcount;

    if (sptcount == 0l) {
      // No point has been smoothed. 
      break;
    } else {
      iter++;
      if (iter == 2) { //if (iter >= b->optpasses) {
        break;
      }
    }

    // Swap the two flip queues.
    swapqueue = flipqueue;
    flipqueue = unflipqueue;
    unflipqueue = swapqueue;
  } // while

  delete flipqueue;

  return totalsptcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// optimizemesh()    Optimize mesh for specified objective functions.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::optimizemesh()
{
  badface *parybface;
  triface checktet;
  point *ppt;
  int optpasses;
  optparameters opm;
  REAL ncosdd[6], maxdd;
  long totalremcount, remcount;
  long totalsmtcount, smtcount;
  long totalsptcount, sptcount;
  int chkencflag;
  int iter;
  int n;

  if (!b->quiet) {
    printf("Optimizing mesh...\n");
  }

  optpasses = ((1 << b->optlevel) - 1);

  if (b->verbose) {
    printf("  Optimization level  = %d.\n", b->optlevel);
    printf("  Optimization scheme = %d.\n", b->optscheme);
    printf("  Min_Max dihed angle = %g.\n", b->optmaxdihedral);
  }

  totalsmtcount = totalsptcount = totalremcount = 0l;

  cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI);
  cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI);
  cosslidihed = cos(b->optminslidihed / 180.0 * PI);

  int attrnum = numelemattrib - 1; 

  // Put all bad tetrahedra into array.
  tetrahedrons->traversalinit();
  checktet.tet = tetrahedrontraverse();
  while (checktet.tet != nullptr) {
    if (b->convex) { // -c
      // Skip this tet if it lies in the exterior.
      if (elemattribute(checktet.tet, attrnum) == -1.0) {
        checktet.tet = tetrahedrontraverse();
        continue;
      }
    }
    ppt = (point *) & (checktet.tet[4]);
    tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, &maxdd, nullptr);
    if (maxdd < cosmaxdihed) {
      // There are bad dihedral angles in this tet.
      unflipqueue->newindex((void **) &parybface); 
      parybface->tt.tet = checktet.tet;
      parybface->tt.ver = 11;
      parybface->forg = ppt[0];
      parybface->fdest = ppt[1];
      parybface->fapex = ppt[2];
      parybface->foppo = ppt[3];
      parybface->key = maxdd;
      for (n = 0; n < 6; n++) {
        parybface->cent[n] = ncosdd[n];
      }
    }
    checktet.tet = tetrahedrontraverse();
  }

  totalremcount = improvequalitybyflips();

  if ((unflipqueue->objects > 0l) && 
      ((b->optscheme & 2) || (b->optscheme & 4))) {
    // The pool is only used by removeslivers().
    badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock,
                                     sizeof(void *), 0);

    // Smoothing options.
    opm.min_max_dihedangle = 1;
    opm.numofsearchdirs = 10;
    // opm.searchstep = 0.001;  
    opm.maxiter = 30; // Limit the maximum iterations.
    //opm.checkencflag = 4; // Queue affected tets after smoothing.
    chkencflag = 4; // Queue affected tets after splitting a sliver.
    iter = 0;

    while (iter < optpasses) {
      smtcount = sptcount = remcount = 0l;
      if (b->optscheme & 2) {
        smtcount += improvequalitybysmoothing(&opm);
        totalsmtcount += smtcount;
        if (smtcount > 0l) {
          remcount = improvequalitybyflips();
          totalremcount += remcount;
        }
      }
      if (unflipqueue->objects > 0l) {
        if (b->optscheme & 4) {
          sptcount += removeslivers(chkencflag);
          totalsptcount += sptcount;
          if (sptcount > 0l) {
            remcount = improvequalitybyflips();
            totalremcount += remcount;
          }
        }
      }
      if (unflipqueue->objects > 0l) {
        if (remcount > 0l) {
          iter++;
        } else {
          break;
        }
      } else {
        break;
      }
    } // while (iter)

    delete badtetrahedrons;

  }

  if (unflipqueue->objects > 0l) {
    if (b->verbose > 1) {
      printf("    %ld bad tets remained.\n", unflipqueue->objects);
    }
    unflipqueue->restart();
  }

  if (b->verbose) {
    if (totalremcount > 0l) {
      printf("  Removed %ld edges.\n", totalremcount);
    }
    if (totalsmtcount > 0l) {
      printf("  Smoothed %ld points.\n", totalsmtcount);
    }
    if (totalsptcount > 0l) {
      printf("  Split %ld slivers.\n", totalsptcount);
    }
  }
}

////                                                                       ////
////                                                                       ////
//// optimize_cxx /////////////////////////////////////////////////////////////

//// meshstat_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// printfcomma()    Print a (large) number with the 'thousands separator'.   // 
//                                                                           //
// The following code was simply copied from "stackoverflow".                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::printfcomma(unsigned long n)
{
  unsigned long n2 = 0;
  int scale = 1;
  while (n >= 1000) {
    n2 = n2 + scale * (n % 1000);
    n /= 1000;
    scale *= 1000;
  }
  printf ("%ld", n);
  while (scale != 1) {
    scale /= 1000;
    n = n2 / scale;
    n2 = n2  % scale;
    printf (",%03ld", n);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkmesh()    Test the mesh for topological consistency.                 //
//                                                                           //
// If 'topoflag' is set, only check the topological connection of the mesh,  //
// i.e., do not report degenerated or inverted elements.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkmesh(int topoflag)
{
  triface tetloop, neightet, symtet;
  point pa, pb, pc, pd;
  REAL ori;
  int horrors, i;

  if (!b->quiet) {
    printf("  Checking consistency of mesh...\n");
  }

  horrors = 0;
  tetloop.ver = 0;
  // Run through the list of tetrahedra, checking each one.
  tetrahedrons->traversalinit();
  tetloop.tet = alltetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Check all four faces of the tetrahedron.
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      pa = org(tetloop);
      pb = dest(tetloop);
      pc = apex(tetloop);
      pd = oppo(tetloop);
      if (tetloop.ver == 0) {  // Only test for inversion once.
        if (!ishulltet(tetloop)) {  // Only do test if it is not a hull tet.
          if (!topoflag) {
            ori = orient3d(pa, pb, pc, pd);
            if (ori >= 0.0) {
              printf("  !! !! %s ", ori > 0.0 ? "Inverted" : "Degenerated");
              printf("  (%d, %d, %d, %d) (ori = %.17g)\n", pointmark(pa),
                     pointmark(pb), pointmark(pc), pointmark(pd), ori);
              horrors++;
            }
          }
        }
        if (infected(tetloop)) { 
          // This may be a bug. Report it.
          printf("  !! (%d, %d, %d, %d) is infected.\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          horrors++;
        }
        if (marktested(tetloop)) {
          // This may be a bug. Report it.
          printf("  !! (%d, %d, %d, %d) is marked.\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          horrors++;
        }
      }
      if (tetloop.tet[tetloop.ver] == nullptr) {
        printf("  !! !! No neighbor at face (%d, %d, %d).\n", pointmark(pa),
               pointmark(pb), pointmark(pc));
        horrors++;
      } else {
        // Find the neighboring tetrahedron on this face.
        fsym(tetloop, neightet);
        // Check that the tetrahedron's neighbor knows it's a neighbor.
        fsym(neightet, symtet);
        if ((tetloop.tet != symtet.tet) || (tetloop.ver != symtet.ver)) {
          printf("  !! !! Asymmetric tetra-tetra bond:\n");
          if (tetloop.tet == symtet.tet) {
            printf("   (Right tetrahedron, wrong orientation)\n");
          }
          printf("    First:  (%d, %d, %d, %d)\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          printf("    Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
                 pointmark(dest(neightet)), pointmark(apex(neightet)),
                 pointmark(oppo(neightet)));
          horrors++;
        }
        // Check if they have the same edge (the bond() operation).
        if ((org(neightet) != pb) || (dest(neightet) != pa)) {
          printf("  !! !! Wrong edge-edge bond:\n");
          printf("    First:  (%d, %d, %d, %d)\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          printf("    Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
                 pointmark(dest(neightet)), pointmark(apex(neightet)),
                 pointmark(oppo(neightet)));
          horrors++;
        }
        // Check if they have the same apex.
        if (apex(neightet) != pc) {
          printf("  !! !! Wrong face-face bond:\n");
          printf("    First:  (%d, %d, %d, %d)\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          printf("    Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
                 pointmark(dest(neightet)), pointmark(apex(neightet)),
                 pointmark(oppo(neightet)));
          horrors++;
        }
        // Check if they have the same opposite.
        if (oppo(neightet) == pd) {
          printf("  !! !! Two identical tetra:\n");
          printf("    First:  (%d, %d, %d, %d)\n", pointmark(pa),
                 pointmark(pb), pointmark(pc), pointmark(pd));
          printf("    Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)),
                 pointmark(dest(neightet)), pointmark(apex(neightet)),
                 pointmark(oppo(neightet)));
          horrors++;
        }
      }
      if (facemarked(tetloop)) {
        // This may be a bug. Report it.
        printf("  !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa),
               pointmark(pb), pointmark(pc), pointmark(pd));
      }
    }
    // Check the six edges of this tet.
    for (i = 0; i < 6; i++) {
      tetloop.ver = edge2ver[i];
      if (edgemarked(tetloop)) {
        // This may be a bug. Report it.
        printf("  !! tetedge (%d, %d) %d, %d is marked.\n", 
               pointmark(org(tetloop)), pointmark(dest(tetloop)), 
               pointmark(apex(tetloop)), pointmark(oppo(tetloop)));
      }
    }
    tetloop.tet = alltetrahedrontraverse();
  }
  if (horrors == 0) {
    if (!b->quiet) {
      printf("  In my studied opinion, the mesh appears to be consistent.\n");
    }
  } else {
    printf("  !! !! !! !! %d %s witnessed.\n", horrors, 
           horrors > 1 ? "abnormity" : "abnormities");
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkshells()       Test the boundary mesh for topological consistency.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkshells()
{
  triface neightet, symtet;
  face shloop, spinsh, nextsh;
  face checkseg;
  point pa, pb;
  int bakcount;
  int horrors, i;

  if (!b->quiet) {
    printf("  Checking consistency of the mesh boundary...\n");
  }
  horrors = 0;

  void **bakpathblock = subfaces->pathblock;
  void *bakpathitem = subfaces->pathitem;
  int bakpathitemsleft = subfaces->pathitemsleft;
  int bakalignbytes = subfaces->alignbytes;

  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != nullptr) {
    shloop.shver = 0;
    for (i = 0; i < 3; i++) {
      // Check the face ring at this edge.
      pa = sorg(shloop);
      pb = sdest(shloop);
      spinsh = shloop;
      spivot(spinsh, nextsh);
      bakcount = horrors;
      while ((nextsh.sh != nullptr) && (nextsh.sh != shloop.sh)) {
        if (nextsh.sh[3] == nullptr) {
          printf("  !! !! Wrong subface-subface connection (Dead subface).\n");
          printf("    First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                 pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                 pointmark(sapex(spinsh)));
          printf("    Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh);
          horrors++;
          break;
        }
        // check if they have the same edge.
        if (!(((sorg(nextsh) == pa) && (sdest(nextsh) == pb)) ||
              ((sorg(nextsh) == pb) && (sdest(nextsh) == pa)))) {
           printf("  !! !! Wrong subface-subface connection.\n");
           printf("    First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                  pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                  pointmark(sapex(spinsh)));
           printf("    Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh,
                  pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), 
                  pointmark(sapex(nextsh)));
           horrors++;
           break;
        }
        // Check they should not have the same apex.
        if (sapex(nextsh) == sapex(spinsh)) {
           printf("  !! !! Existing two duplicated subfaces.\n");
           printf("    First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                  pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                  pointmark(sapex(spinsh)));
           printf("    Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh,
                  pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), 
                  pointmark(sapex(nextsh)));
           horrors++;
           break;
        }
        spinsh = nextsh;
        spivot(spinsh, nextsh);
      }
      // Check subface-subseg bond.
      sspivot(shloop, checkseg);
      if (checkseg.sh != nullptr) {
        if (checkseg.sh[3] == nullptr) {
          printf("  !! !! Wrong subface-subseg connection (Dead subseg).\n");
          printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                 pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                 pointmark(sapex(shloop)));
          printf("    Sub: x%lx (Dead)\n", (uintptr_t) checkseg.sh);
          horrors++;
        } else {
          if (!(((sorg(checkseg) == pa) && (sdest(checkseg) == pb)) ||
                ((sorg(checkseg) == pb) && (sdest(checkseg) == pa)))) {
            printf("  !! !! Wrong subface-subseg connection.\n");
            printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                   pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                   pointmark(sapex(shloop)));
            printf("    Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh,
                   pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
            horrors++;
          }
        }
      }
      if (horrors > bakcount) break; // An error detected. 
      senextself(shloop);
    }
    // Check tet-subface connection.
    stpivot(shloop, neightet);
    if (neightet.tet != nullptr) {
      if (neightet.tet[4] == nullptr) {
        printf("  !! !! Wrong sub-to-tet connection (Dead tet)\n");
        printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh,
               pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
               pointmark(sapex(shloop)));
        printf("    Tet: x%lx (DEAD)\n", (uintptr_t) neightet.tet);
        horrors++;
      } else {
        if (!((sorg(shloop) == org(neightet)) && 
              (sdest(shloop) == dest(neightet)))) {
          printf("  !! !! Wrong sub-to-tet connection\n");
          printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                 pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                 pointmark(sapex(shloop)));
          printf("    Tet: x%lx (%d, %d, %d, %d).\n",
                 (uintptr_t) neightet.tet, pointmark(org(neightet)), 
                 pointmark(dest(neightet)), pointmark(apex(neightet)),
                 pointmark(oppo(neightet)));
          horrors++;
        }
        tspivot(neightet, spinsh);
        if (!((sorg(spinsh) == org(neightet)) && 
              (sdest(spinsh) == dest(neightet)))) {
          printf("  !! !! Wrong tet-sub connection.\n");
          printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                 pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                 pointmark(sapex(spinsh)));
          printf("    Tet: x%lx (%d, %d, %d, %d).\n", 
                 (uintptr_t) neightet.tet, pointmark(org(neightet)), 
                 pointmark(dest(neightet)), pointmark(apex(neightet)), 
                 pointmark(oppo(neightet)));
          horrors++;
        }
        fsym(neightet, symtet);
        tspivot(symtet, spinsh);
        if (spinsh.sh != nullptr) {
          if (!((sorg(spinsh) == org(symtet)) && 
                (sdest(spinsh) == dest(symtet)))) {
            printf("  !! !! Wrong tet-sub connection.\n");
            printf("    Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                   pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                   pointmark(sapex(spinsh)));
            printf("    Tet: x%lx (%d, %d, %d, %d).\n", 
                   (uintptr_t) symtet.tet, pointmark(org(symtet)), 
                   pointmark(dest(symtet)), pointmark(apex(symtet)), 
                   pointmark(oppo(symtet)));
            horrors++;
          }
        } else {
          printf("  Warning: Broken tet-sub-tet connection.\n");
        }
      }
    }
    if (sinfected(shloop)) {
      // This may be a bug. report it.
      printf("  !! A infected subface: (%d, %d, %d).\n", 
             pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
             pointmark(sapex(shloop)));
    }
    if (smarktested(shloop)) {
      // This may be a bug. report it.
      printf("  !! A marked subface: (%d, %d, %d).\n", pointmark(sorg(shloop)), 
             pointmark(sdest(shloop)), pointmark(sapex(shloop)));
    }
    shloop.sh = shellfacetraverse(subfaces);
  }

  if (horrors == 0) {
    if (!b->quiet) {
      printf("  Mesh boundaries connected correctly.\n");
    }
  } else {
    printf("  !! !! !! !! %d boundary connection viewed with horror.\n",
           horrors);
  }

  subfaces->pathblock = bakpathblock;
  subfaces->pathitem = bakpathitem;
  subfaces->pathitemsleft = bakpathitemsleft;
  subfaces->alignbytes = bakalignbytes;

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checksegments()    Check the connections between tetrahedra and segments. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checksegments()
{
  triface tetloop, neightet, spintet;
  shellface *segs;
  face neighsh, spinsh, checksh;
  face sseg, checkseg;
  point pa, pb;
  int miscount;
  int t1ver;
  int horrors, i;


  if (!b->quiet) {
    printf("  Checking tet->seg connections...\n");
  }

  horrors = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != nullptr) {
    // Loop the six edges of the tet.
    if (tetloop.tet[8] != nullptr) {
      segs = (shellface *) tetloop.tet[8];
      for (i = 0; i < 6; i++) {
        sdecode(segs[i], sseg);
        if (sseg.sh != nullptr) {
          // Get the edge of the tet.
          tetloop.ver = edge2ver[i];
          // Check if they are the same edge.
          pa = (point) sseg.sh[3];
          pb = (point) sseg.sh[4];
          if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) ||
                ((org(tetloop) == pb) && (dest(tetloop) == pa)))) {
            printf("  !! Wrong tet-seg connection.\n");
            printf("    Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", 
                   (uintptr_t) tetloop.tet, pointmark(org(tetloop)),
                   pointmark(dest(tetloop)), pointmark(apex(tetloop)),
                   pointmark(oppo(tetloop)), (uintptr_t) sseg.sh,
                   pointmark(pa), pointmark(pb));
            horrors++;
          } else {
            // Loop all tets sharing at this edge.
            neightet = tetloop;
            do {
              tsspivot1(neightet, checkseg);
              if (checkseg.sh != sseg.sh) {
                printf("  !! Wrong tet->seg connection.\n");
                printf("    Tet: x%lx (%d, %d, %d, %d) - ", 
                       (uintptr_t) neightet.tet, pointmark(org(neightet)),
                       pointmark(dest(neightet)), pointmark(apex(neightet)),
                       pointmark(oppo(neightet)));
                if (checkseg.sh != nullptr) {
                  printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh,
                         pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); 
                } else {
                  printf("Seg: nullptr.\n");
                }
                horrors++;
              }
              fnextself(neightet);
            } while (neightet.tet != tetloop.tet);
          }
          // Check the seg->tet pointer.
          sstpivot1(sseg, neightet);
          if (neightet.tet == nullptr) {
            printf("  !! Wrong seg->tet connection (A nullptr tet).\n");
            horrors++;
          } else {
            if (!(((org(neightet) == pa) && (dest(neightet) == pb)) ||
                ((org(neightet) == pb) && (dest(neightet) == pa)))) {
              printf("  !! Wrong seg->tet connection (Wrong edge).\n");
              printf("    Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", 
                     (uintptr_t) neightet.tet, pointmark(org(neightet)),
                     pointmark(dest(neightet)), pointmark(apex(neightet)),
                     pointmark(oppo(neightet)), (uintptr_t) sseg.sh,
                     pointmark(pa), pointmark(pb));
              horrors++;
            }
          }
        }
      }
    }
    // Loop the six edge of this tet.
    neightet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      neightet.ver = edge2ver[i];
      if (edgemarked(neightet)) {
        // A possible bug. Report it.
        printf("  !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n",
               pointmark(org(neightet)), pointmark(dest(neightet)),
               pointmark(apex(neightet)), pointmark(oppo(neightet)),
               (uintptr_t) neightet.tet, neightet.ver);
        // Check if all tets at the edge are marked.
        spintet = neightet;
        while (1) {
          fnextself(spintet);
          if (!edgemarked(spintet)) {
            printf("  !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n",
                   pointmark(org(spintet)), pointmark(dest(spintet)),
                   pointmark(apex(spintet)), pointmark(oppo(spintet)),
                   (uintptr_t) spintet.tet, spintet.ver);
            horrors++;
          }
          if (spintet.tet == neightet.tet) break;
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (!b->quiet) {
    printf("  Checking seg->tet connections...\n");
  }

  miscount = 0; // Count the number of unrecovered segments.
  subsegs->traversalinit();
  sseg.shver = 0;
  sseg.sh = shellfacetraverse(subsegs);
  while (sseg.sh != nullptr) {
    pa = sorg(sseg);
    pb = sdest(sseg);
    spivot(sseg, neighsh);
    if (neighsh.sh != nullptr) {      
      spinsh = neighsh;
      while (1) {
        // Check seg-subface bond.
        if (((sorg(spinsh) == pa) && (sdest(spinsh) == pb)) ||
            ((sorg(spinsh) == pb) && (sdest(spinsh) == pa))) {
          // Keep the same rotate direction.
          //if (sorg(spinsh) != pa) {          
          //  sesymself(spinsh);
          //  printf("  !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n",
          //         pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
          //         pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh,
          //         spinsh.shver);
          //  horrors++;
          //}
          stpivot(spinsh, spintet);
          if (spintet.tet != nullptr) {
            // Check if all tets at this segment.
            while (1) {
              tsspivot1(spintet, checkseg);
              if (checkseg.sh == nullptr) {
                printf("  !! !! No seg at tet (%d, %d, %d, %d) -- x%lx %d\n",
                       pointmark(org(spintet)), pointmark(dest(spintet)),
                       pointmark(apex(spintet)), pointmark(oppo(spintet)),
                       (uintptr_t) spintet.tet, spintet.ver);
                horrors++;
              }
              if (checkseg.sh != sseg.sh) {
                printf("  !! !! Wrong seg (%d, %d) at tet (%d, %d, %d, %d)\n",
                       pointmark(sorg(checkseg)), pointmark(sdest(checkseg)),
                       pointmark(org(spintet)), pointmark(dest(spintet)),
                       pointmark(apex(spintet)), pointmark(oppo(spintet)));
                horrors++;
              }
              fnextself(spintet);
              // Stop at the next subface.
              tspivot(spintet, checksh);
              if (checksh.sh != nullptr) break;
            } // while (1)
          }
        } else { 
          printf("  !! Wrong seg-subface (%d, %d, %d) -- x%lx %d connect\n",
                 pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
                 pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh,
                 spinsh.shver);
          horrors++;
          break;
        } // if pa, pb
        spivotself(spinsh);
        if (spinsh.sh == nullptr) break; // A dangling segment.
        if (spinsh.sh == neighsh.sh) break;
      } // while (1)
    } // if (neighsh.sh != nullptr)
    // Count the number of "un-recovered" segments.
    sstpivot1(sseg, neightet);
    if (neightet.tet == nullptr) {
      miscount++;
    }
    sseg.sh = shellfacetraverse(subsegs);
  }

  if (!b->quiet) {
    printf("  Checking seg->seg connections...\n");
  }

  points->traversalinit();
  pa = pointtraverse();
  while (pa != nullptr) {
    if (pointtype(pa) == FREESEGVERTEX) {
      // There should be two subsegments connected at 'pa'.
      // Get a subsegment containing 'pa'.
      sdecode(point2sh(pa), sseg);
      if ((sseg.sh == nullptr) || sseg.sh[3] == nullptr) {
        printf("  !! Dead point-to-seg pointer at point %d.\n",
               pointmark(pa));
        horrors++;
      } else {
        sseg.shver = 0;
        if (sorg(sseg) != pa) {
          if (sdest(sseg) != pa) {
            printf("  !! Wrong point-to-seg pointer at point %d.\n",
                   pointmark(pa));
            horrors++;
          } else {
            // Find the next subsegment at 'pa'.
            senext(sseg, checkseg);
            if ((checkseg.sh == nullptr) || (checkseg.sh[3] == nullptr)) {
              printf("  !! Dead seg-seg connection at point %d.\n",
                     pointmark(pa));
              horrors++;
            } else {
              spivotself(checkseg);
              checkseg.shver = 0;
              if (sorg(checkseg) != pa) {
                printf("  !! Wrong seg-seg connection at point %d.\n",
                     pointmark(pa));
                horrors++;
              }
            }
          }
        } else {
          // Find the previous subsegment at 'pa'.
          senext2(sseg, checkseg);
          if ((checkseg.sh == nullptr) || (checkseg.sh[3] == nullptr)) {
            printf("  !! Dead seg-seg connection at point %d.\n",
                   pointmark(pa));
            horrors++;
          } else {
            spivotself(checkseg);
            checkseg.shver = 0;
            if (sdest(checkseg) != pa) {
              printf("  !! Wrong seg-seg connection at point %d.\n",
                     pointmark(pa));
              horrors++;
            }
          }
        }
      }
    }
    pa = pointtraverse();
  }

  if (horrors == 0) {
    printf("  Segments are connected properly.\n");
  } else {
    printf("  !! !! !! !! Found %d missing connections.\n", horrors);
  }
  if (miscount > 0) {
    printf("  !! !! Found %d missing segments.\n", miscount);
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkdelaunay()    Ensure that the mesh is (constrained) Delaunay.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkdelaunay()
{
  triface tetloop;
  triface symtet;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL sign;
  int ndcount; // Count the non-locally Delaunay faces.
  int horrors;

  if (!b->quiet) {
    printf("  Checking Delaunay property of the mesh...\n");
  }

  ndcount = 0;
  horrors = 0;
  tetloop.ver = 0;
  // Run through the list of triangles, checking each one.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Check all four faces of the tetrahedron.
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      fsym(tetloop, symtet);
      // Only do test if its adjoining tet is not a hull tet or its pointer
      //   is larger (to ensure that each pair isn't tested twice).
      if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) {
        pa = org(tetloop);
        pb = dest(tetloop);
        pc = apex(tetloop);
        pd = oppo(tetloop);
        pe = oppo(symtet);
        sign = insphere_s(pa, pb, pc, pd, pe);
        if (sign < 0.0) {
          ndcount++;
          if (checksubfaceflag) {
            tspivot(tetloop, checksh);
          }
          if (checksh.sh == nullptr) {
            printf("  !! Non-locally Delaunay (%d, %d, %d) - %d, %d\n",
                   pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
                   pointmark(pe));
            horrors++;
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (horrors == 0) {
    if (!b->quiet) {
      if (ndcount > 0) {
        printf("  The mesh is constrained Delaunay.\n");
      } else {
        printf("  The mesh is Delaunay.\n");
      } 
    }
  } else {
    printf("  !! !! !! !! Found %d non-Delaunay faces.\n", horrors);
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Check if the current tetrahedralization is (constrained) regular.         //
//                                                                           //
// The parameter 'type' determines which regularity should be checked:       //
//   - 0:  check the Delaunay property.                                      //
//   - 1:  check the Delaunay property with symbolic perturbation.           //
//   - 2:  check the regular property, the weights are stored in p[3].       //
//   - 3:  check the regular property with symbolic perturbation.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkregular(int type)
{
  triface tetloop;
  triface symtet;
  face checksh;
  point p[5];
  REAL sign;
  int ndcount; // Count the non-locally Delaunay faces.
  int horrors;

  if (!b->quiet) {
    printf("  Checking %s %s property of the mesh...\n",
           (type & 2) == 0 ? "Delaunay" : "regular",
           (type & 1) == 0 ? " " : "(s)");
  }

  // Make sure orient3d(p[1], p[0], p[2], p[3]) > 0;
  //   Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that
  //     p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3].
  //   The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that
  //     p[4] lies below the oriented hyperplane passing through 
  //     p[1], p[0], p[2], p[3].

  ndcount = 0;
  horrors = 0;
  tetloop.ver = 0;
  // Run through the list of triangles, checking each one.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Check all four faces of the tetrahedron.
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      fsym(tetloop, symtet);
      // Only do test if its adjoining tet is not a hull tet or its pointer
      //   is larger (to ensure that each pair isn't tested twice).
      if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) {
        p[0] = org(tetloop);   // pa
        p[1] = dest(tetloop);  // pb
        p[2] = apex(tetloop);  // pc
        p[3] = oppo(tetloop);  // pd
        p[4] = oppo(symtet);   // pe

        if (type == 0) {
          sign = insphere(p[1], p[0], p[2], p[3], p[4]);
        } else if (type == 1) {
          sign = insphere_s(p[1], p[0], p[2], p[3], p[4]);
        } else if (type == 2) {
          sign = orient4d(p[1],    p[0],    p[2],    p[3],    p[4], 
                          p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]);
        } else { // type == 3
          sign = orient4d_s(p[1],    p[0],    p[2],    p[3],    p[4], 
                            p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]);
        }

        if (sign > 0.0) {
          ndcount++;
          if (checksubfaceflag) {
            tspivot(tetloop, checksh);
          }
          if (checksh.sh == nullptr) {
            printf("  !! Non-locally %s (%d, %d, %d) - %d, %d\n",
                   (type & 2) == 0 ? "Delaunay" : "regular",
                   pointmark(p[0]), pointmark(p[1]), pointmark(p[2]), 
                   pointmark(p[3]), pointmark(p[4]));
            horrors++;
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (horrors == 0) {
    if (!b->quiet) {
      if (ndcount > 0) {
        printf("  The mesh is constrained %s.\n",
               (type & 2) == 0 ? "Delaunay" : "regular");
      } else {
        printf("  The mesh is %s.\n", (type & 2) == 0 ? "Delaunay" : "regular");
      } 
    }
  } else {
    printf("  !! !! !! !! Found %d non-%s faces.\n", horrors,
           (type & 2) == 0 ? "Delaunay" : "regular");
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkconforming()    Ensure that the mesh is conforming Delaunay.         //
//                                                                           //
// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces.   //
// If 'flag' is 3, check both subsegments and subfaces.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkconforming(int flag)
{
  triface searchtet, neightet, spintet;
  face shloop;
  face segloop;
  point eorg, edest, eapex, pa, pb, pc;
  REAL cent[3], radius, dist, diff, rd, len;
  bool enq;
  int encsubsegs, encsubfaces;
  int t1ver; 
  int i;

  REAL A[4][4], rhs[4], D;
  int indx[4];
  REAL elen[3];

  encsubsegs = 0;

  if (flag & 1) {
    if (!b->quiet) {
      printf("  Checking conforming property of segments...\n");
    }
    encsubsegs = 0;

    // Run through the list of subsegments, check each one.
    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != (shellface *) nullptr) {
      eorg = (point) segloop.sh[3];
      edest = (point) segloop.sh[4];
      radius = 0.5 * distance(eorg, edest);
      for (i = 0; i < 3; i++) cent[i] = 0.5 * (eorg[i] + edest[i]);

      enq = false;
      sstpivot1(segloop, neightet);
      if (neightet.tet != nullptr) {
        spintet = neightet;
        while (1) {
          eapex= apex(spintet);
          if (eapex != dummypoint) {
            dist = distance(eapex, cent);
            diff = dist - radius;
            if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
            if (diff < 0) {
              enq = true; break;
            }
          }
          fnextself(spintet);
          if (spintet.tet == neightet.tet) break;
        }
      }
      if (enq) {
        printf("  !! !! Non-conforming segment: (%d, %d)\n",
               pointmark(eorg), pointmark(edest));
        encsubsegs++;
      }
      segloop.sh = shellfacetraverse(subsegs);
    }

    if (encsubsegs == 0) {
      if (!b->quiet) {
        printf("  The segments are conforming Delaunay.\n");
      }
    } else {
      printf("  !! !! %d subsegments are non-conforming.\n", encsubsegs);
    }
  } // if (flag & 1)

  encsubfaces = 0;

  if (flag & 2) {
    if (!b->quiet) {
      printf("  Checking conforming property of subfaces...\n");
    }

    // Run through the list of subfaces, check each one.
    subfaces->traversalinit();
    shloop.sh = shellfacetraverse(subfaces);
    while (shloop.sh != (shellface *) nullptr) {
      pa = (point) shloop.sh[3];
      pb = (point) shloop.sh[4];
      pc = (point) shloop.sh[5];

      // Compute the coefficient matrix A (3x3).
      A[0][0] = pb[0] - pa[0];
      A[0][1] = pb[1] - pa[1];
      A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
      A[1][0] = pc[0] - pa[0];
      A[1][1] = pc[1] - pa[1];
      A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)
      cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)

      // Compute the right hand side vector b (3x1).
      elen[0] = dot(A[0], A[0]);
      elen[1] = dot(A[1], A[1]);
      rhs[0] = 0.5 * elen[0];
      rhs[1] = 0.5 * elen[1];
      rhs[2] = 0.0;

      if (lu_decmp(A, 3, indx, &D, 0)) {
        lu_solve(A, 3, indx, rhs, 0);
        cent[0] = pa[0] + rhs[0];
        cent[1] = pa[1] + rhs[1];
        cent[2] = pa[2] + rhs[2];
        rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);

        // Check if this subface is encroached.
        for (i = 0; i < 2; i++) {
          stpivot(shloop, searchtet);
          if (!ishulltet(searchtet)) {
            len = distance(oppo(searchtet), cent);
            if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding.
            if (len < rd) {
              printf("  !! !! Non-conforming subface: (%d, %d, %d)\n",
                     pointmark(pa), pointmark(pb), pointmark(pc));
              encsubfaces++;
              enq = true; break;
            }
          }
          sesymself(shloop);
        }
      }
      shloop.sh = shellfacetraverse(subfaces);
    }

    if (encsubfaces == 0) {
      if (!b->quiet) {
        printf("  The subfaces are conforming Delaunay.\n");
      }
    } else {
      printf("  !! !! %d subfaces are non-conforming.\n", encsubfaces);
    }
  } // if (flag & 2)

  return encsubsegs + encsubfaces;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// qualitystatistics()    Print statistics about the quality of the mesh.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::qualitystatistics()
{
  triface tetloop, neightet;
  point p[4];
  char sbuf[128];
  REAL radiusratiotable[12];
  REAL aspectratiotable[12];
  REAL A[4][4], rhs[4], D;
  REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights.
  REAL edgelength[6], alldihed[6], faceangle[3];
  REAL shortest, longest;
  REAL smallestvolume, biggestvolume;
  REAL smallestratio, biggestratio;
  REAL smallestdiangle, biggestdiangle;
  REAL smallestfaangle, biggestfaangle;
  REAL total_tet_vol, total_tetprism_vol;
  REAL tetvol, minaltitude;
  REAL cirradius, minheightinv; // insradius;
  REAL shortlen, longlen;
  REAL tetaspect, tetradius;
  REAL smalldiangle, bigdiangle;
  REAL smallfaangle, bigfaangle;
  int radiustable[12];
  int aspecttable[16];
  int dihedangletable[18];
  int faceangletable[18];
  int indx[4];
  int radiusindex;
  int aspectindex;
  int tendegree;
  int i, j;

  printf("Mesh quality statistics:\n\n");

  shortlen = longlen = 0.0;
  smalldiangle = bigdiangle = 0.0;
  total_tet_vol = 0.0;
  total_tetprism_vol = 0.0;

  radiusratiotable[0]  =    0.707;    radiusratiotable[1]  =     1.0;
  radiusratiotable[2]  =      1.1;    radiusratiotable[3]  =     1.2;
  radiusratiotable[4]  =      1.4;    radiusratiotable[5]  =     1.6;
  radiusratiotable[6]  =      1.8;    radiusratiotable[7]  =     2.0;
  radiusratiotable[8]  =      2.5;    radiusratiotable[9]  =     3.0;
  radiusratiotable[10] =     10.0;    radiusratiotable[11] =     0.0;

  aspectratiotable[0]  =      1.5;    aspectratiotable[1]  =     2.0;
  aspectratiotable[2]  =      2.5;    aspectratiotable[3]  =     3.0;
  aspectratiotable[4]  =      4.0;    aspectratiotable[5]  =     6.0;
  aspectratiotable[6]  =     10.0;    aspectratiotable[7]  =    15.0;
  aspectratiotable[8]  =     25.0;    aspectratiotable[9]  =    50.0;
  aspectratiotable[10] =    100.0;    aspectratiotable[11] =     0.0;
  
  for (i = 0; i < 12; i++) radiustable[i] = 0;
  for (i = 0; i < 12; i++) aspecttable[i] = 0;
  for (i = 0; i < 18; i++) dihedangletable[i] = 0;
  for (i = 0; i < 18; i++) faceangletable[i] = 0;

  minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
  minaltitude = minaltitude * minaltitude;
  shortest = minaltitude;
  longest = 0.0;
  smallestvolume = minaltitude;
  biggestvolume = 0.0;
  smallestratio = 1e+16; // minaltitude;
  biggestratio = 0.0;
  smallestdiangle = smallestfaangle = 180.0;
  biggestdiangle = biggestfaangle = 0.0;


  int attrnum = numelemattrib - 1;

  // Loop all elements, calculate quality parameters for each element.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {

    if (b->convex) {
      // Skip tets in the exterior.
      if (elemattribute(tetloop.tet, attrnum) == -1.0) {
        tetloop.tet = tetrahedrontraverse();
        continue;
      }
    }

    // Get four vertices: p0, p1, p2, p3.
    for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i];

    // Get the tet volume.
    tetvol = orient3dfast(p[1], p[0], p[2], p[3]) / 6.0;
    total_tet_vol += tetvol;
    total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]);

    // Calculate the largest and smallest volume.
    if (tetvol < smallestvolume) {
      smallestvolume = tetvol;
    } 
    if (tetvol > biggestvolume) {
      biggestvolume = tetvol;
    }

    // Set the edge vectors: V[0], ..., V[5]
    for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0.
    for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1.
    for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2.
    for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1.
    for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2.
    for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0.

    // Get the squares of the edge lengthes.
    for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]);

    // Calculate the longest and shortest edge length.
    for (i = 0; i < 6; i++) {
      if (i == 0) {
        shortlen = longlen = edgelength[i];
      } else {
        shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
        longlen  = edgelength[i] > longlen  ? edgelength[i] : longlen;
      }
      if (edgelength[i] > longest) {
        longest = edgelength[i];
      } 
      if (edgelength[i] < shortest) {
        shortest = edgelength[i];
      }
    }

    // Set the matrix A = [V[0], V[1], V[2]]^T.
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) A[j][i] = V[j][i];
    }

    // Decompose A just once.
    if (lu_decmp(A, 3, indx, &D, 0)) {   
      // Get the three faces normals.
      for (j = 0; j < 3; j++) {
        for (i = 0; i < 3; i++) rhs[i] = 0.0;
        rhs[j] = 1.0;  // Positive means the inside direction
        lu_solve(A, 3, indx, rhs, 0);
        for (i = 0; i < 3; i++) N[j][i] = rhs[i];
      }
      // Get the fourth face normal by summing up the first three.
      for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
      // Get the radius of the circumsphere.
      for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]);
      lu_solve(A, 3, indx, rhs, 0);
      cirradius = sqrt(dot(rhs, rhs));
      // Normalize the face normals.
      for (i = 0; i < 4; i++) {
        // H[i] is the inverse of height of its corresponding face.
        H[i] = sqrt(dot(N[i], N[i]));
        for (j = 0; j < 3; j++) N[i][j] /= H[i];
      }
      // Get the radius of the inscribed sphere.
      // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
      // Get the biggest H[i] (corresponding to the smallest height).
      minheightinv = H[0];
      for (i = 1; i < 3; i++) {
        if (H[i] > minheightinv) minheightinv = H[i];
      }
    } else {
      // A nearly degenerated tet.
      if (tetvol <= 0.0) {
        // assert(tetvol != 0.0);
        printf("  !! Warning:  A %s tet (%d,%d,%d,%d).\n", 
               tetvol < 0 ? "inverted" : "degenerated", pointmark(p[0]),
               pointmark(p[1]), pointmark(p[2]), pointmark(p[3]));
        // Skip it.        
        tetloop.tet = tetrahedrontraverse();
        continue;
      }
      // Calculate the four face normals.
      facenormal(p[2], p[1], p[3], N[0], 1, nullptr);
      facenormal(p[0], p[2], p[3], N[1], 1, nullptr);
      facenormal(p[1], p[0], p[3], N[2], 1, nullptr);
      facenormal(p[0], p[1], p[2], N[3], 1, nullptr);
      // Normalize the face normals.
      for (i = 0; i < 4; i++) {
        // H[i] is the twice of the area of the face.
        H[i] = sqrt(dot(N[i], N[i]));
        for (j = 0; j < 3; j++) N[i][j] /= H[i];
      }
      // Get the biggest H[i] / tetvol (corresponding to the smallest height).
      minheightinv = (H[0] / tetvol);
      for (i = 1; i < 3; i++) {
        if ((H[i] / tetvol) > minheightinv) minheightinv = (H[i] / tetvol);
      }
      // Let the circumradius to be the half of its longest edge length.
      cirradius = 0.5 * sqrt(longlen);
    }

    // Get the dihedrals (in degree) at each edges.
    j = 0;
    for (i = 1; i < 4; i++) {
      alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc.
      if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
      else if (alldihed[j] > 1.0) alldihed[j] = 1;
      alldihed[j] = acos(alldihed[j]) / PI * 180.0;
      j++;
    }
    for (i = 2; i < 4; i++) {
      alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac.
      if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
      else if (alldihed[j] > 1.0) alldihed[j] = 1;
      alldihed[j] = acos(alldihed[j]) / PI * 180.0;
      j++;
    }
    alldihed[j] = -dot(N[2], N[3]); // Edge ab.
    if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
    else if (alldihed[j] > 1.0) alldihed[j] = 1;
    alldihed[j] = acos(alldihed[j]) / PI * 180.0;

    // Calculate the largest and smallest dihedral angles.
    for (i = 0; i < 6; i++) {
      if (i == 0) {
        smalldiangle = bigdiangle = alldihed[i];
      } else {
        smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle;
        bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle;
      }
      if (alldihed[i] < smallestdiangle) {
        smallestdiangle = alldihed[i];
      } 
      if (alldihed[i] > biggestdiangle) {
        biggestdiangle = alldihed[i];
      }
      // Accumulate the corresponding number in the dihedral angle histogram.
      if (alldihed[i] < 5.0) {
        tendegree = 0;
      } else if (alldihed[i] >= 5.0 && alldihed[i] < 10.0) {
        tendegree = 1;
      } else if (alldihed[i] >= 80.0 && alldihed[i] < 110.0) {
        tendegree = 9; // Angles between 80 to 110 degree are in one entry.
      } else if (alldihed[i] >= 170.0 && alldihed[i] < 175.0) {
        tendegree = 16;
      } else if (alldihed[i] >= 175.0) {
        tendegree = 17;
      } else {
        tendegree = (int) (alldihed[i] / 10.);
        if (alldihed[i] < 80.0) {
          tendegree++;  // In the left column.
        } else {
          tendegree--;  // In the right column.
        }
      }
      dihedangletable[tendegree]++;
    }



    // Calulate the largest and smallest face angles.
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      fsym(tetloop, neightet);
      // Only do the calulation once for a face.
      if (((point) neightet.tet[7] == dummypoint) || 
          (tetloop.tet < neightet.tet)) {
        p[0] = org(tetloop);
        p[1] = dest(tetloop);
        p[2] = apex(tetloop);
        faceangle[0] = interiorangle(p[0], p[1], p[2], nullptr);
        faceangle[1] = interiorangle(p[1], p[2], p[0], nullptr);
        faceangle[2] = PI - (faceangle[0] + faceangle[1]);
        // Translate angles into degrees.
        for (i = 0; i < 3; i++) {
          faceangle[i] = (faceangle[i] * 180.0) / PI;
        }
        // Calculate the largest and smallest face angles.
        for (i = 0; i < 3; i++) {
          if (i == 0) {
            smallfaangle = bigfaangle = faceangle[i];
          } else {
            smallfaangle = faceangle[i] < smallfaangle ? 
              faceangle[i] : smallfaangle;
            bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle;
          }
          if (faceangle[i] < smallestfaangle) {
            smallestfaangle = faceangle[i];
          } 
          if (faceangle[i] > biggestfaangle) {
            biggestfaangle = faceangle[i];
          }
          tendegree = (int) (faceangle[i] / 10.);
          faceangletable[tendegree]++;
        }
      }
    }

    // Calculate aspect ratio and radius-edge ratio for this element.
    tetradius = cirradius / sqrt(shortlen);
    // tetaspect = sqrt(longlen) / (2.0 * insradius);
    tetaspect = sqrt(longlen) * minheightinv;
    // Remember the largest and smallest aspect ratio.
    if (tetaspect < smallestratio) {
      smallestratio = tetaspect;
    } 
    if (tetaspect > biggestratio) {
      biggestratio = tetaspect;
    }
    // Accumulate the corresponding number in the aspect ratio histogram.
    aspectindex = 0;
    while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) {
      aspectindex++;
    }
    aspecttable[aspectindex]++;
    radiusindex = 0;
    while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) {
      radiusindex++;
    }
    radiustable[radiusindex]++;

    tetloop.tet = tetrahedrontraverse();
  }

  shortest = sqrt(shortest);
  longest = sqrt(longest);
  minaltitude = sqrt(minaltitude);

  printf("  Smallest volume: %16.5g   |  Largest volume: %16.5g\n",
         smallestvolume, biggestvolume);
  printf("  Shortest edge:   %16.5g   |  Longest edge:   %16.5g\n",
         shortest, longest);
  printf("  Smallest asp.ratio: %13.5g   |  Largest asp.ratio: %13.5g\n",
         smallestratio, biggestratio);
  sprintf(sbuf, "%.17g", biggestfaangle);
  if (strlen(sbuf) > 8) {
    sbuf[8] = '\0';
  }
  printf("  Smallest facangle: %14.5g   |  Largest facangle:       %s\n",
         smallestfaangle, sbuf);
  sprintf(sbuf, "%.17g", biggestdiangle);
  if (strlen(sbuf) > 8) {
    sbuf[8] = '\0';
  }
  printf("  Smallest dihedral: %14.5g   |  Largest dihedral:       %s\n\n",
         smallestdiangle, sbuf);

  printf("  Aspect ratio histogram:\n");
  printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
         aspectratiotable[0], aspecttable[0], aspectratiotable[5],
         aspectratiotable[6], aspecttable[6]);
  for (i = 1; i < 5; i++) {
    printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
           aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
           aspectratiotable[i + 5], aspectratiotable[i + 6],
           aspecttable[i + 6]);
  }
  printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
         aspectratiotable[4], aspectratiotable[5], aspecttable[5],
         aspectratiotable[10], aspecttable[11]);
  printf("  (A tetrahedron's aspect ratio is its longest edge length");
  printf(" divided by its\n");
  printf("    smallest side height)\n\n");

  printf("  Face angle histogram:\n");
  for (i = 0; i < 9; i++) {
    printf("    %3d - %3d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           i * 10, i * 10 + 10, faceangletable[i],
           i * 10 + 90, i * 10 + 100, faceangletable[i + 9]);
  }
  if (minfaceang != PI) {
    printf("  Minimum input face angle is %g (degree).\n",
           minfaceang / PI * 180.0);
  }
  printf("\n");

  printf("  Dihedral angle histogram:\n");
  // Print the three two rows:
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         5, 10, dihedangletable[1], 110, 120, dihedangletable[10]);
  // Print the third to seventh rows.
  for (i = 2; i < 7; i++) {
    printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           (i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i],
           (i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]);
  }
  // Print the last two rows.
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         70, 80, dihedangletable[8], 175, 180, dihedangletable[17]);
  if (minfacetdihed != PI) {
    printf("  Minimum input dihedral angle is %g (degree).\n",
           minfacetdihed / PI * 180.0);
  }
  printf("\n");

  printf("\n");
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// memorystatistics()    Report the memory usage.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorystatistics()
{
  printf("Memory usage statistics:\n\n");
 
  // Count the number of blocks of tetrahedra. 
  int tetblocks = 0;
  tetrahedrons->pathblock = tetrahedrons->firstblock;
  while (tetrahedrons->pathblock != nullptr) {
    tetblocks++;
    tetrahedrons->pathblock = (void **) *(tetrahedrons->pathblock);  
  }

  // Calculate the total memory (in bytes) used by storing meshes.
  unsigned long totalmeshmemory = 0l, totalt2shmemory = 0l;
  totalmeshmemory = points->maxitems * points->itembytes +
                    tetrahedrons->maxitems * tetrahedrons->itembytes;
  if (b->plc || b->refine) {
    totalmeshmemory += (subfaces->maxitems * subfaces->itembytes +
                        subsegs->maxitems * subsegs->itembytes);
    totalt2shmemory = (tet2subpool->maxitems * tet2subpool->itembytes +
                       tet2segpool->maxitems * tet2segpool->itembytes);
  }

  unsigned long totalalgomemory = 0l;
  totalalgomemory = cavetetlist->totalmemory + cavebdrylist->totalmemory +
                    caveoldtetlist->totalmemory + 
                    flippool->maxitems * flippool->itembytes;
  if (b->plc || b->refine) {
    totalalgomemory += (subsegstack->totalmemory + subfacstack->totalmemory +
                        subvertstack->totalmemory + 
                        caveshlist->totalmemory + caveshbdlist->totalmemory +
                        cavesegshlist->totalmemory +
                        cavetetshlist->totalmemory + 
                        cavetetseglist->totalmemory +
                        caveencshlist->totalmemory +
                        caveencseglist->totalmemory +
                        cavetetvertlist->totalmemory +
                        unflipqueue->totalmemory);
  }

  printf("  Maximum number of tetrahedra:  %ld\n", tetrahedrons->maxitems);
  printf("  Maximum number of tet blocks (blocksize = %d):  %d\n",
         b->tetrahedraperblock, tetblocks);
  /*
  if (b->plc || b->refine) {
    printf("  Approximate memory for tetrahedral mesh (bytes):  %ld\n",
           totalmeshmemory);
    
    printf("  Approximate memory for extra pointers (bytes):  %ld\n",
           totalt2shmemory);
  } else {
    printf("  Approximate memory for tetrahedralization (bytes):  %ld\n",
           totalmeshmemory);
  }
  printf("  Approximate memory for algorithms (bytes):  %ld\n",
         totalalgomemory);
  printf("  Approximate memory for working arrays (bytes):  %ld\n",
         totalworkmemory);
  printf("  Approximate total used memory (bytes):  %ld\n",
         totalmeshmemory + totalt2shmemory + totalalgomemory + 
         totalworkmemory);
  */
  if (b->plc || b->refine) {
    printf("  Approximate memory for tetrahedral mesh (bytes):  ");
    printfcomma(totalmeshmemory); printf("\n");
    
    printf("  Approximate memory for extra pointers (bytes):  ");
    printfcomma(totalt2shmemory); printf("\n");
  } else {
    printf("  Approximate memory for tetrahedralization (bytes):  ");
    printfcomma(totalmeshmemory); printf("\n");
  }
  printf("  Approximate memory for algorithms (bytes):  ");
  printfcomma(totalalgomemory); printf("\n");
  printf("  Approximate memory for working arrays (bytes):  ");
  printfcomma(totalworkmemory); printf("\n");
  printf("  Approximate total used memory (bytes):  ");
  printfcomma(totalmeshmemory + totalt2shmemory + totalalgomemory + 
              totalworkmemory);
  printf("\n");

  printf("\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// statistics()    Print all sorts of cool facts.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::statistics()
{
  long tetnumber, facenumber;

  printf("\nStatistics:\n\n");
  printf("  Input points: %d\n", in->numberofpoints);
  if (b->refine) {
    printf("  Input tetrahedra: %d\n", in->numberoftetrahedra);
  }
  if (b->plc) {
    printf("  Input facets: %d\n", in->numberoffacets);
    printf("  Input segments: %ld\n", insegments);
    printf("  Input holes: %d\n", in->numberofholes);
    printf("  Input regions: %d\n", in->numberofregions);
  }

  tetnumber = tetrahedrons->items - hullsize;
  facenumber = (tetnumber * 4l + hullsize) / 2l;

  if (b->weighted) { // -w option
    printf("\n  Mesh points: %ld\n", points->items - nonregularcount);
  } else {
    printf("\n  Mesh points: %ld\n", points->items);
  }
  printf("  Mesh tetrahedra: %ld\n", tetnumber);
  printf("  Mesh faces: %ld\n", facenumber);
  if (meshedges > 0l) {
    printf("  Mesh edges: %ld\n", meshedges);
  } else {
    if (!nonconvex) {
      long vsize = points->items - dupverts - unuverts;
      if (b->weighted) vsize -= nonregularcount;
      meshedges = vsize + facenumber - tetnumber - 1;
      printf("  Mesh edges: %ld\n", meshedges);
    }
  }

  if (b->plc || b->refine) {
    printf("  Mesh boundary faces: %ld\n", subfaces->items);
    printf("  Mesh boundary edges: %ld\n", subsegs->items);
    if (st_segref_count > 0l) {
      printf("  Steiner points on boundary edges:  %ld\n", st_segref_count);
    }
    if (st_facref_count > 0l) {
      printf("  Steiner points on boundary faces:  %ld\n", st_facref_count);
    }
    if (st_volref_count > 0l) {
      printf("  Steiner points inside mesh domain: %ld\n", st_volref_count);
    }
  } else {
    printf("  Convex hull faces: %ld\n", hullsize);
    if (meshhulledges > 0l) {
      printf("  Convex hull edges: %ld\n", meshhulledges);
    }
  }
  if (b->weighted) { // -w option
    printf("  Skipped non-regular points: %ld\n", nonregularcount);
  }
  printf("\n");


  if (b->verbose > 0) {
    if (b->plc || b->refine) { // -p or -r
      if (tetrahedrons->items > 0l) {
        qualitystatistics();
      }
    }
    if (tetrahedrons->items > 0l) {
      memorystatistics();
    }
  }
}

////                                                                       ////
////                                                                       ////
//// meshstat_cxx /////////////////////////////////////////////////////////////

//// output_cxx ///////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// jettisonnodes()    Jettison unused or duplicated vertices.                //
//                                                                           //
// Unused points are those input points which are outside the mesh domain or //
// have no connection (isolated) to the mesh.  Duplicated points exist for   //
// example if the input PLC is read from a .stl mesh file (marked during the //
// Delaunay tetrahedralization step. This routine remove these points from   //
// points list. All existing points are reindexed.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::jettisonnodes()
{
  point pointloop;
  bool jetflag;
  int oldidx, newidx;
  int remcount;

  if (!b->quiet) {
    printf("Jettisoning redundants points.\n");
  }

  points->traversalinit();
  pointloop = pointtraverse();
  oldidx = newidx = 0; // in->firstnumber;
  remcount = 0;
  while (pointloop != (point) nullptr) {
    jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || 
      (pointtype(pointloop) == UNUSEDVERTEX);
    if (jetflag) {
      // It is a duplicated or unused point, delete it.
      pointdealloc(pointloop);
      remcount++;
    } else {
      // Re-index it.
      setpointmark(pointloop, newidx + in->firstnumber);
      if (in->pointmarkerlist != (int *) nullptr) {
        if (oldidx < in->numberofpoints) {
          // Re-index the point marker as well.
          in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx];
        }
      }
      newidx++;
    }
    oldidx++;
    pointloop = pointtraverse();
  }
  if (b->verbose) {
    printf("  %ld duplicated vertices are removed.\n", dupverts);
    printf("  %ld unused vertices are removed.\n", unuverts);
  }
  dupverts = 0l;
  unuverts = 0l;

  // The following line ensures that dead items in the pool of nodes cannot
  //   be allocated for the new created nodes. This ensures that the input
  //   nodes will occur earlier in the output files, and have lower indices.
  points->deaditemstack = (void *) nullptr;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// highorder()   Create extra nodes for quadratic subparametric elements.    //
//                                                                           //
// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing  //
// high-order nodes of each tetrahedron.  This routine is used only when -o2 //
// switch is used.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::highorder()
{
  triface tetloop, worktet, spintet;
  point *extralist, *adjextralist;
  point torg, tdest, newpoint;
  int highorderindex;
  int t1ver;
  int i, j;

  if (!b->quiet) {
    printf("Adding vertices for second-order tetrahedra.\n");
  }

  // Initialize the 'highordertable'.
  highordertable = new point[tetrahedrons->items * 6];
  if (highordertable == (point *) nullptr) {
    terminatetetgen(1);
  }

  // This will overwrite the slot for element markers.
  highorderindex = 11;

  // The following line ensures that dead items in the pool of nodes cannot
  //   be allocated for the extra nodes associated with high order elements.
  //   This ensures that the primary nodes (at the corners of elements) will
  //   occur earlier in the output files, and have lower indices, than the
  //   extra nodes.
  points->deaditemstack = (void *) nullptr;

  // Assign an entry for each tetrahedron to find its extra nodes. At the
  //   mean while, initialize all extra nodes be nullptr.
  i = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i];
    for (j = 0; j < 6; j++) {
      highordertable[i + j] = (point) nullptr;
    }
    i += 6;
    tetloop.tet = tetrahedrontraverse();
  }

  // To create a unique node on each edge. Loop over all tetrahedra, and
  //   look at the six edges of each tetrahedron.  If the extra node in
  //   the tetrahedron corresponding to this edge is nullptr, create a node
  //   for this edge, at the same time, set the new node into the extra
  //   node lists of all other tetrahedra sharing this edge.  
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Get the list of extra nodes.
    extralist = (point *) tetloop.tet[highorderindex];
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      if (extralist[i] == (point) nullptr) {
        // Go to the ith-edge.
        worktet.ver = edge2ver[i];
        // Create a new point in the middle of this edge.
        torg = org(worktet);
        tdest = dest(worktet);
        makepoint(&newpoint, FREEVOLVERTEX);
        for (j = 0; j < 3 + numpointattrib; j++) {
          newpoint[j] = 0.5 * (torg[j] + tdest[j]);
        }
        // Interpolate its metrics.
        for (j = 0; j < in->numberofpointmtrs; j++) {
          newpoint[pointmtrindex + j] = 
            0.5 * (torg[pointmtrindex + j] + tdest[pointmtrindex + j]);
        }
        // Set this point into all extra node lists at this edge.
        spintet = worktet;
        while (1) {
          if (!ishulltet(spintet)) {
            adjextralist = (point *) spintet.tet[highorderindex];
            adjextralist[ver2edge[spintet.ver]] = newpoint;
          }
          fnextself(spintet);
          if (spintet.tet == worktet.tet) break;
        }
      } // if (!extralist[i])
    } // i
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// numberedges()    Count the number of edges, save in "meshedges".          //
//                                                                           //
// This routine is called when '-p' or '-r', and '-E' options are used.  The //
// total number of edges depends on the genus of the input surface mesh.     //
//                                                                           //
// NOTE:  This routine must be called after outelements().  So all elements  //
// have been indexed.                                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::numberedges()
{
  triface worktet, spintet;
  int ishulledge;
  int t1ver;
  int i;

  meshedges = meshhulledges = 0l;

  tetrahedrons->traversalinit();
  worktet.tet = tetrahedrontraverse();
  while (worktet.tet != nullptr) {
    // Count the number of Voronoi faces. Look at the six edges of this
    //   tet. Count an edge only if this tet's index is smaller than
    //   those of other non-hull tets which share this edge.
    for (i = 0; i < 6; i++) {
      worktet.ver = edge2ver[i];
      ishulledge = 0;
      fnext(worktet, spintet);
      do {
        if (!ishulltet(spintet)) {          
          if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
        } else {
          ishulledge = 1;
        }
        fnextself(spintet);
      } while (spintet.tet != worktet.tet);
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet == worktet.tet) {
        meshedges++;
        if (ishulledge) meshhulledges++;
      }
    }
    worktet.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outnodes()    Output the points to a .node file or a tetgenio structure.  //
//                                                                           //
// Note: each point has already been numbered on input (the first index is   //
// 'in->firstnumber').                                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outnodes(tetgenio* out)
{
  FILE *outfile = nullptr;
  char outnodefilename[FILENAMESIZE];
  face parentsh;
  point pointloop;
  int nextras, bmark, marker = 0, weightDT = 0; 
  int coordindex, attribindex;
  int pointnumber, firstindex;
  int index, i;

  if (out == (tetgenio *) nullptr) {
    strcpy(outnodefilename, b->outfilename);
    strcat(outnodefilename, ".node");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outnodefilename);
    } else {
      printf("Writing nodes.\n");
    }
  }

  nextras = numpointattrib;
  if (b->weighted) { // -w
    if (b->weighted_param == 0) weightDT = 1; // Weighted DT.
  }

  bmark = !b->nobound && in->pointmarkerlist;

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outnodefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outnodefilename);
      terminatetetgen(1);
    }
    // Number of points, number of dimensions, number of point attributes,
    //   and number of boundary markers (zero or one).
    fprintf(outfile, "%ld  %d  %d  %d\n", points->items, 3, nextras, bmark);
  } else {
    // Allocate space for 'pointlist';
    out->pointlist = new REAL[points->items * 3];
    if (out->pointlist == (REAL *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    // Allocate space for 'pointattributelist' if necessary;
    if (nextras > 0) {
      out->pointattributelist = new REAL[points->items * nextras];
      if (out->pointattributelist == (REAL *) nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    // Allocate space for 'pointmarkerlist' if necessary;
    if (bmark) {
      out->pointmarkerlist = new int[points->items];
      if (out->pointmarkerlist == (int *) nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    if (b->psc) {
      out->pointparamlist = new tetgenio::pointparam[points->items];
      if (out->pointparamlist == nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberofpoints = points->items;
    out->numberofpointattributes = nextras;
    coordindex = 0;
    attribindex = 0;
  }
  
  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;

  points->traversalinit();
  pointloop = pointtraverse();
  pointnumber = firstindex; // in->firstnumber;
  index = 0;
  while (pointloop != (point) nullptr) {
    if (bmark) {
      // Default the vertex has a zero marker.
      marker = 0;
      // Is it an input vertex?
      if (index < in->numberofpoints) {
        // Input point's marker is directly copied to output.
        marker = in->pointmarkerlist[index];       
      } else {
        if ((pointtype(pointloop) == FREESEGVERTEX) ||
            (pointtype(pointloop) == FREEFACETVERTEX)) {
          sdecode(point2sh(pointloop), parentsh);
          if (parentsh.sh != nullptr) {
            marker = shellmark(parentsh);
            if (pointtype(pointloop) == FREEFACETVERTEX) {
              if (in->facetmarkerlist != nullptr) {
                marker = in->facetmarkerlist[marker - 1];
              }
            }
          }
        } // if (pointtype(...))
      }
    }
    if (out == (tetgenio *) nullptr) {
      // Point number, x, y and z coordinates.
      fprintf(outfile, "%4d    %.17g  %.17g  %.17g", pointnumber,
              pointloop[0], pointloop[1], pointloop[2]);
      for (i = 0; i < nextras; i++) {
        // Write an attribute.
        if ((i == 0) && weightDT) {          
          fprintf(outfile, "  %.17g", pointloop[0] * pointloop[0] +
             pointloop[1] * pointloop[1] + pointloop[2] * pointloop[2] 
             - pointloop[3 + i]);
        } else { 
          fprintf(outfile, "  %.17g", pointloop[3 + i]);
        }
      }
      if (bmark) {
        // Write the boundary marker.
        fprintf(outfile, "    %d", marker);
      }
      if (b->psc) {
        fprintf(outfile, "  %.8g  %.8g  %d", pointgeomuv(pointloop, 0),
                pointgeomuv(pointloop, 1), pointgeomtag(pointloop));
        if (pointtype(pointloop) == RIDGEVERTEX) {
          fprintf(outfile, "  0");
        } else if (pointtype(pointloop) == ACUTEVERTEX) {
          fprintf(outfile, "  0");
        } else if (pointtype(pointloop) == FREESEGVERTEX) {
          fprintf(outfile, "  1");
        } else if (pointtype(pointloop) == FREEFACETVERTEX) {
          fprintf(outfile, "  2");
        } else if (pointtype(pointloop) == FREEVOLVERTEX) {
          fprintf(outfile, "  3");
        } else {
          fprintf(outfile, "  -1"); // Unknown type.
        }
      }
      fprintf(outfile, "\n");
    } else {
      // X, y, and z coordinates.
      out->pointlist[coordindex++] = pointloop[0];
      out->pointlist[coordindex++] = pointloop[1];
      out->pointlist[coordindex++] = pointloop[2];
      // Point attributes.
      for (i = 0; i < nextras; i++) {
        // Output an attribute.
        if ((i == 0) && weightDT) {
          out->pointattributelist[attribindex++] = 
            pointloop[0] * pointloop[0] + pointloop[1] * pointloop[1] + 
            pointloop[2] * pointloop[2] - pointloop[3 + i];
        } else {
          out->pointattributelist[attribindex++] = pointloop[3 + i];
        }
      }
      if (bmark) {
        // Output the boundary marker.  
        out->pointmarkerlist[index] = marker;
      }
      if (b->psc) {
        out->pointparamlist[index].uv[0] = pointgeomuv(pointloop, 0);
        out->pointparamlist[index].uv[1] = pointgeomuv(pointloop, 1);
        out->pointparamlist[index].tag = pointgeomtag(pointloop);
        if (pointtype(pointloop) == RIDGEVERTEX) {
          out->pointparamlist[index].type = 0;
        } else if (pointtype(pointloop) == ACUTEVERTEX) {
          out->pointparamlist[index].type = 0;
        } else if (pointtype(pointloop) == FREESEGVERTEX) {
          out->pointparamlist[index].type = 1;
        } else if (pointtype(pointloop) == FREEFACETVERTEX) {
          out->pointparamlist[index].type = 2;
        } else if (pointtype(pointloop) == FREEVOLVERTEX) {
          out->pointparamlist[index].type = 3;
        } else {
          out->pointparamlist[index].type = -1; // Unknown type.
        }
      }
    }
    pointloop = pointtraverse();
    pointnumber++; 
    index++;
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmetrics()    Output the metric to a file (*.mtr) or a tetgenio obj.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmetrics(tetgenio* out)
{
  FILE *outfile = nullptr;
  char outmtrfilename[FILENAMESIZE];
  point ptloop;
  int mtrindex;

  if (out == (tetgenio *) nullptr) {
    strcpy(outmtrfilename, b->outfilename);
    strcat(outmtrfilename, ".mtr");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outmtrfilename);
    } else {
      printf("Writing metrics.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outmtrfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outmtrfilename);
      terminatetetgen(3);
    }
    // Number of points, number of point metrices,
    // fprintf(outfile, "%ld  %d\n", points->items, sizeoftensor + 3);
    fprintf(outfile, "%ld  %d\n", points->items, 1);
  } else {
    // Allocate space for 'pointmtrlist' if necessary;
    // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)];
    out->pointmtrlist = new REAL[points->items];
    if (out->pointmtrlist == (REAL *) nullptr) {
      terminatetetgen(1);
    }
    out->numberofpointmtrs = 1; // (sizeoftensor + 3);
    mtrindex = 0;
  }

  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) nullptr) {
    if (out == (tetgenio *) nullptr) {
      // for (i = 0; i < sizeoftensor; i++) {
      //   fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]);
      // }
      fprintf(outfile, "%-16.8e\n", ptloop[pointmtrindex]);
    } else {
      // for (i = 0; i < sizeoftensor; i++) {
      //   out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
      // }
      out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex];
    }
    ptloop = pointtraverse();
  }
  
  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outelements()    Output the tetrahedra to an .ele file or a tetgenio      //
//                  structure.                                               //
//                                                                           //
// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> //
// firstnumber). The total number of mesh edges is counted in 'meshedges'.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outelements(tetgenio* out)
{
  FILE *outfile = nullptr;
  char outelefilename[FILENAMESIZE];
  tetrahedron* tptr;
  point p1, p2, p3, p4;
  point *extralist;
  REAL *talist = nullptr;
  int *tlist = nullptr;
  long ntets;
  int firstindex, shift;
  int pointindex, attribindex;
  int highorderindex = 11; 
  int elementnumber;
  int eextras;
  int i;

  if (out == (tetgenio *) nullptr) {
    strcpy(outelefilename, b->outfilename);
    strcat(outelefilename, ".ele");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outelefilename);
    } else {
      printf("Writing elements.\n");
    }
  }

  // The number of tets excluding hull tets.
  ntets = tetrahedrons->items - hullsize;

  eextras = numelemattrib;
  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outelefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outelefilename);
      terminatetetgen(1);
    }
    // Number of tetras, points per tetra, attributes per tetra.
    fprintf(outfile, "%ld  %d  %d\n", ntets, b->order == 1 ? 4 : 10, eextras);
  } else {
    // Allocate memory for output tetrahedra.
    out->tetrahedronlist = new int[ntets * (b->order == 1 ? 4 : 10)];
    if (out->tetrahedronlist == (int *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    // Allocate memory for output tetrahedron attributes if necessary.
    if (eextras > 0) {
      out->tetrahedronattributelist = new REAL[ntets * eextras];
      if (out->tetrahedronattributelist == (REAL *) nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberoftetrahedra = ntets;
    out->numberofcorners = b->order == 1 ? 4 : 10;
    out->numberoftetrahedronattributes = eextras;
    tlist = out->tetrahedronlist;
    talist = out->tetrahedronattributelist;
    pointindex = 0;
    attribindex = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tptr = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tptr != (tetrahedron *) nullptr) {
    if (!b->reversetetori) {
      p1 = (point) tptr[4];
      p2 = (point) tptr[5];
    } else {
      p1 = (point) tptr[5];
      p2 = (point) tptr[4];
    }
    p3 = (point) tptr[6];
    p4 = (point) tptr[7];
    if (out == (tetgenio *) nullptr) {
      // Tetrahedron number, indices for four points.
      fprintf(outfile, "%5d   %5d %5d %5d %5d", elementnumber,
              pointmark(p1) - shift, pointmark(p2) - shift,
              pointmark(p3) - shift, pointmark(p4) - shift);
      if (b->order == 2) {
        extralist = (point *) tptr[highorderindex];
        // indices for six extra points.
        fprintf(outfile, "  %5d %5d %5d %5d %5d %5d",
          pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift,
          pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift,
          pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift);
      }
      for (i = 0; i < eextras; i++) {
        fprintf(outfile, "    %.17g", elemattribute(tptr, i));
      }
      fprintf(outfile, "\n");
    } else {
      tlist[pointindex++] = pointmark(p1) - shift;
      tlist[pointindex++] = pointmark(p2) - shift;
      tlist[pointindex++] = pointmark(p3) - shift;
      tlist[pointindex++] = pointmark(p4) - shift;
      if (b->order == 2) {
        extralist = (point *) tptr[highorderindex];
        tlist[pointindex++] = pointmark(extralist[0]) - shift;
        tlist[pointindex++] = pointmark(extralist[1]) - shift;
        tlist[pointindex++] = pointmark(extralist[2]) - shift;
        tlist[pointindex++] = pointmark(extralist[3]) - shift;
        tlist[pointindex++] = pointmark(extralist[4]) - shift;
        tlist[pointindex++] = pointmark(extralist[5]) - shift;
      }
      for (i = 0; i < eextras; i++) {
        talist[attribindex++] = elemattribute(tptr, i);
      }
    }
    // Remember the index of this element (for counting edges).
    setelemindex(tptr, elementnumber);
    tptr = tetrahedrontraverse();
    elementnumber++;
  }


  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outfaces()    Output all faces to a .face file or a tetgenio object.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outfaces(tetgenio* out)
{
  FILE *outfile = nullptr;
  char facefilename[FILENAMESIZE];
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  long ntets, faces;
  int *elist = nullptr, *emlist = nullptr;
  int neigh1 = 0, neigh2 = 0;
  int faceid, marker = 0;
  int firstindex, shift;
  int facenumber;
  int index = 0;

  // For -o2 option.
  triface workface;
  point *extralist, pp[3] = {0,0,0}; 
  int highorderindex = 11; 
  int o2index = 0, i;

  if (out == (tetgenio *) nullptr) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  ntets = tetrahedrons->items - hullsize;
  faces = (ntets * 4l + hullsize) / 2l;

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(1);
    }
    fprintf(outfile, "%ld  %d\n", faces, !b->nobound);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[faces * 3];
    if (out->trifacelist == (int *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    if (b->order == 2) {
      out->o2facelist = new int[faces * 3];
    }
    // Allocate memory for 'trifacemarkerlist' if necessary.
    if (!b->nobound) {
      out->trifacemarkerlist = new int[faces];
      if (out->trifacemarkerlist == (int *) nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[faces * 2];
      if (out->adjtetlist == (int *) nullptr) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = faces;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  facenumber = firstindex; // in->firstnumber;
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each one. If its adjacent tet is a hull tet,
  //   operate on the face, otherwise, operate on the face only if the
  //   current tet has a smaller index than its neighbor.
  while (tface.tet != (tetrahedron *) nullptr) {
    for (tface.ver = 0; tface.ver < 4; tface.ver ++) {
      fsym(tface, tsymface);
      if (ishulltet(tsymface) || 
          (elemindex(tface.tet) < elemindex(tsymface.tet))) {
        torg = org(tface);
        tdest = dest(tface);
        tapex = apex(tface);
        if (b->order == 2) { // -o2
          // Get the three extra vertices on edges.
          extralist = (point *) (tface.tet[highorderindex]);
          // The extra vertices are on edges opposite the corners.
          enext(tface, workface);
          for (i = 0; i < 3; i++) {
            pp[i] = extralist[ver2edge[workface.ver]];
            enextself(workface);
          }
        }
        if (!b->nobound) {
          // Get the boundary marker of this face.
          if (b->plc || b->refine) { 
            // Shell face is used.
            tspivot(tface, checkmark);
            if (checkmark.sh == nullptr) {
              marker = 0;  // It is an inner face. It's marker is 0.
            } else {
              if (in->facetmarkerlist) {
                // The facet marker is given, get it.
                faceid = shellmark(checkmark) - 1;
                marker = in->facetmarkerlist[faceid];
              } else {
                marker = 1; // The default marker for subface is 1.
              }
            }
          } else {
            // Shell face is not used, only distinguish outer and inner face.
            marker = (int) ishulltet(tsymface);
          }
        }
        if (b->neighout > 1) {
          // '-nn' switch. Output adjacent tets indices.
          neigh1 = elemindex(tface.tet);
          if (!ishulltet(tsymface)) {
            neigh2 = elemindex(tsymface.tet);
          } else {
            neigh2 = -1;  
          }
        }
        if (out == (tetgenio *) nullptr) {
          // Face number, indices of three vertices.
          fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift,
                  pointmark(tapex) - shift);
          if (b->order == 2) { // -o2
            fprintf(outfile, "  %4d  %4d  %4d", pointmark(pp[0]) - shift, 
                    pointmark(pp[1]) - shift, pointmark(pp[2]) - shift);
          }
          if (!b->nobound) {
            // Output a boundary marker.
            fprintf(outfile, "  %d", marker);
          }
          if (b->neighout > 1) {
            fprintf(outfile, "    %5d  %5d", neigh1, neigh2);
          }
          fprintf(outfile, "\n");
        } else {
          // Output indices of three vertices.
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
          elist[index++] = pointmark(tapex) - shift;
          if (b->order == 2) { // -o2
            out->o2facelist[o2index++] = pointmark(pp[0]) - shift;
            out->o2facelist[o2index++] = pointmark(pp[1]) - shift;
            out->o2facelist[o2index++] = pointmark(pp[2]) - shift;
          }
          if (!b->nobound) {
            emlist[facenumber - in->firstnumber] = marker;
          }
          if (b->neighout > 1) {
            out->adjtetlist[(facenumber - in->firstnumber) * 2]     = neigh1;
            out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
          }
        }
        facenumber++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outhullfaces()    Output hull faces to a .face file or a tetgenio object. //
//                                                                           //
// The normal of each face is pointing to the outside of the domain.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outhullfaces(tetgenio* out)
{
  FILE *outfile = nullptr;
  char facefilename[FILENAMESIZE];
  triface hulltet;
  point torg, tdest, tapex;
  int *elist = nullptr;
  int firstindex, shift;
  int facenumber;
  int index;

  if (out == (tetgenio *) nullptr) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(1);
    }
    fprintf(outfile, "%ld  0\n", hullsize);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[hullsize * 3];
    if (out->trifacelist == (int *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    out->numberoftrifaces = hullsize;
    elist = out->trifacelist;
    index = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  hulltet.tet = alltetrahedrontraverse();
  facenumber = firstindex;
  while (hulltet.tet != (tetrahedron *) nullptr) {
    if (ishulltet(hulltet)) {
      torg = (point) hulltet.tet[4];
      tdest = (point) hulltet.tet[5];
      tapex = (point) hulltet.tet[6];
      if (out == (tetgenio *) nullptr) {
        // Face number, indices of three vertices.
        fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
                pointmark(torg) - shift, pointmark(tdest) - shift,
                pointmark(tapex) - shift);
        fprintf(outfile, "\n");
      } else {
        // Output indices of three vertices.
        elist[index++] = pointmark(torg) - shift;
        elist[index++] = pointmark(tdest) - shift;
        elist[index++] = pointmark(tapex) - shift;
      }
      facenumber++;
    }
    hulltet.tet = alltetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsubfaces()    Output subfaces (i.e. boundary faces) to a .face file or //
//                  a tetgenio structure.                                    //
//                                                                           //
// The boundary faces are found in 'subfaces'. For listing triangle vertices //
// in the same sense for all triangles in the mesh, the direction determined //
// by right-hand rule is pointer to the inside of the volume.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsubfaces(tetgenio* out)
{
  FILE *outfile = nullptr;
  char facefilename[FILENAMESIZE];
  int *elist = nullptr;
  int *emlist = nullptr;
  int index = 0, index1 = 0, index2 = 0;
  triface abuttingtet;
  face faceloop;
  point torg, tdest, tapex;
  int faceid = 0, marker = 0;
  int firstindex, shift;
  int neigh1 = 0, neigh2 = 0;
  int facenumber;

  // For -o2 option.
  triface workface;
  point *extralist, pp[3] = {0,0,0}; 
  int highorderindex = 11;
  int o2index = 0, i;

  int t1ver; // used by fsymself()

  if (out == (tetgenio *) nullptr) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(3);
    }
    // Number of subfaces.
    fprintf(outfile, "%ld  %d\n", subfaces->items, !b->nobound);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[subfaces->items * 3];
    if (out->trifacelist == (int *) nullptr) {
      terminatetetgen(1);
    }
    if (b->order == 2) {
      out->o2facelist = new int[subfaces->items * 3];
    }
    if (!b->nobound) {
      // Allocate memory for 'trifacemarkerlist'.
      out->trifacemarkerlist = new int[subfaces->items];
      if (out->trifacemarkerlist == (int *) nullptr) {
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[subfaces->items * 2];
      if (out->adjtetlist == (int *) nullptr) {
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = subfaces->items;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  facenumber = firstindex; // in->firstnumber;
  while (faceloop.sh != (shellface *) nullptr) {
    stpivot(faceloop, abuttingtet);
    // If there is a tetrahedron containing this subface, orient it so
    //   that the normal of this face points to inside of the volume by
    //   right-hand rule.
    if (abuttingtet.tet != nullptr) {
      if (ishulltet(abuttingtet)) {
        fsymself(abuttingtet);
        assert(!ishulltet(abuttingtet));
      }
    }
    if (abuttingtet.tet != nullptr) {
      torg = org(abuttingtet);
      tdest = dest(abuttingtet);
      tapex = apex(abuttingtet);
      if (b->order == 2) { // -o2
        // Get the three extra vertices on edges.
        extralist = (point *) (abuttingtet.tet[highorderindex]);
        workface = abuttingtet;
        for (i = 0; i < 3; i++) {
          pp[i] = extralist[ver2edge[workface.ver]];
          enextself(workface);
        }
      }
    } else {
      // This may happen when only a surface mesh be generated.
      torg = sorg(faceloop);
      tdest = sdest(faceloop);
      tapex = sapex(faceloop);
      if (b->order == 2) { // -o2
        // There is no extra node list available.
        pp[0] = torg;
        pp[1] = tdest;
        pp[2] = tapex;
      }
    }
    if (!b->nobound) {
      if (in->facetmarkerlist) {
        faceid = shellmark(faceloop) - 1;
        marker = in->facetmarkerlist[faceid];
      } else {
        marker = 1; // Default marker for a subface is 1.
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch. Output adjacent tets indices.
      neigh1 = -1;
      neigh2 = -1;
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != nullptr) {
        neigh1 = elemindex(abuttingtet.tet);
        fsymself(abuttingtet);
        if (!ishulltet(abuttingtet)) {
          neigh2 = elemindex(abuttingtet.tet);
        }
      }
    }
    if (out == (tetgenio *) nullptr) {
      fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift,
              pointmark(tapex) - shift);
      if (b->order == 2) { // -o2
        fprintf(outfile, "  %4d  %4d  %4d", pointmark(pp[0]) - shift, 
                pointmark(pp[1]) - shift, pointmark(pp[2]) - shift);
      }
      if (!b->nobound) {
        fprintf(outfile, "    %d", marker);
      }
      if (b->neighout > 1) {
        fprintf(outfile, "    %5d  %5d", neigh1, neigh2);
      }
      fprintf(outfile, "\n");
    } else {
      // Output three vertices of this face;
      elist[index++] = pointmark(torg) - shift;
      elist[index++] = pointmark(tdest) - shift;
      elist[index++] = pointmark(tapex) - shift;
      if (b->order == 2) { // -o2
        out->o2facelist[o2index++] = pointmark(pp[0]) - shift;
        out->o2facelist[o2index++] = pointmark(pp[1]) - shift;
        out->o2facelist[o2index++] = pointmark(pp[2]) - shift;
      }
      if (!b->nobound) {
        emlist[index1++] = marker;
      }
      if (b->neighout > 1) {
        out->adjtetlist[index2++] = neigh1;
        out->adjtetlist[index2++] = neigh2;
      }
    }
    facenumber++;
    faceloop.sh = shellfacetraverse(subfaces);
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outedges()    Output all edges to a .edge file or a tetgenio object.      //
//                                                                           //
// Note: This routine must be called after outelements(),  so that the total //
// number of edges 'meshedges' has been counted.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outedges(tetgenio* out)
{
  FILE *outfile = nullptr;
  char edgefilename[FILENAMESIZE];
  triface tetloop, worktet, spintet;
  face checkseg;
  point torg, tdest;
  int *elist = nullptr, *emlist = nullptr;
  int ishulledge;
  int firstindex, shift;
  int edgenumber, marker;
  int index = 0, index1 = 0, index2 = 0;
  int t1ver;
  int i;

  // For -o2 option.
  point *extralist, pp = nullptr; 
  int highorderindex = 11;
  int o2index = 0;

  if (out == (tetgenio *) nullptr) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", edgefilename);
    } else {
      printf("Writing edges.\n");
    }
  }

  if (meshedges == 0l) {
    if (nonconvex) {
      numberedges();  // Count the edges.
    } else {
      // Use Euler's characteristic to get the numbe of edges.
      // It states V - E + F - C = 1, hence E = V + F - C - 1.
      long tsize = tetrahedrons->items - hullsize;
      long fsize = (tsize * 4l + hullsize) / 2l;
      long vsize = points->items - dupverts - unuverts;
      if (b->weighted) vsize -= nonregularcount;
      meshedges = vsize + fsize - tsize - 1;
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(1);
    }
    // Write the number of edges, boundary markers (0 or 1).
    fprintf(outfile, "%ld  %d\n", meshedges, !b->nobound);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[meshedges * 2];
    if (out->edgelist == (int *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    if (b->order == 2) { // -o2 switch
      out->o2edgelist = new int[meshedges];
    }
    if (!b->nobound) {
      out->edgemarkerlist = new int[meshedges];
    }
    if (b->neighout > 1) { // '-nn' switch.
      out->edgeadjtetlist = new int[meshedges];
    }
    out->numberofedges = meshedges;
    elist = out->edgelist;
    emlist = out->edgemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift (reduce) the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  edgenumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Count the number of Voronoi faces. 
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      worktet.ver = edge2ver[i];
      ishulledge = 0;
      fnext(worktet, spintet);
      do {
        if (!ishulltet(spintet)) {
          if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
        } else {
          ishulledge = 1;
        }
        fnextself(spintet);
      } while (spintet.tet != worktet.tet);
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet == worktet.tet) {
        torg = org(worktet);
        tdest = dest(worktet);
        if (b->order == 2) { // -o2
          // Get the extra vertex on this edge.
          extralist = (point *) worktet.tet[highorderindex];
          pp = extralist[ver2edge[worktet.ver]];
        }
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, "%5d   %4d  %4d", edgenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift);
          if (b->order == 2) { // -o2
            fprintf(outfile, "  %4d", pointmark(pp) - shift);
          }
        } else {
          // Output three vertices of this face;
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
          if (b->order == 2) { // -o2
            out->o2edgelist[o2index++] = pointmark(pp) - shift;
          }
        }
        if (!b->nobound) {
          if (b->plc || b->refine) {
            // Check if the edge is a segment.
            tsspivot1(worktet, checkseg);
            if (checkseg.sh != nullptr) {
              marker = shellmark(checkseg);
              if (marker == 0) {  // Does it have no marker?
                marker = 1;  // Set the default marker for this segment.
              }
            } else {
              marker = 0;  // It's not a segment.
            }
          } else {
            // Mark it if it is a hull edge.
            marker = ishulledge ? 1 : 0;
          }
          if (out == (tetgenio *) nullptr) {
            fprintf(outfile, "  %d", marker);
          } else {
            emlist[index1++] = marker;
          }
        }
        if (b->neighout > 1) { // '-nn' switch.
          if (out == (tetgenio *) nullptr) {
            fprintf(outfile, "  %d", elemindex(tetloop.tet));
          } else {
            out->edgeadjtetlist[index2++] = elemindex(tetloop.tet);
          }
        }
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, "\n");
        }
        edgenumber++;
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsubsegments()    Output segments to a .edge file or a structure.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsubsegments(tetgenio* out)
{
  FILE *outfile = nullptr;
  char edgefilename[FILENAMESIZE];
  int *elist = nullptr;
  int index, i;
  face edgeloop;
  point torg, tdest;
  int firstindex, shift;
  int marker;
  int edgenumber;

  // For -o2 option.
  triface workface, spintet;
  point *extralist, pp = nullptr; 
  int highorderindex = 11;
  int o2index = 0;

  // For -nn option.
  int neigh = -1;
  int index2 = 0;

  int t1ver; // used by fsymself()

  if (out == (tetgenio *) nullptr) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", edgefilename);
    } else {
      printf("Writing edges.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(3);
    }
    // Number of subsegments.
    fprintf(outfile, "%ld  1\n", subsegs->items);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[subsegs->items * (b->order == 1 ? 2 : 3)];
    if (out->edgelist == (int *) nullptr) {
      terminatetetgen(1);
    }
    if (b->order == 2) {
      out->o2edgelist = new int[subsegs->items];
    }
    out->edgemarkerlist = new int[subsegs->items];
    if (out->edgemarkerlist == (int *) nullptr) {
      terminatetetgen(1);
    }
    if (b->neighout > 1) {
      out->edgeadjtetlist = new int[subsegs->items];
    }
    out->numberofedges = subsegs->items;
    elist = out->edgelist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }
  index = 0;
  i = 0;

  subsegs->traversalinit();
  edgeloop.sh = shellfacetraverse(subsegs);
  edgenumber = firstindex; // in->firstnumber;
  while (edgeloop.sh != (shellface *) nullptr) {
    torg = sorg(edgeloop);
    tdest = sdest(edgeloop);
    if ((b->order == 2) || (b->neighout > 1)) {
      sstpivot1(edgeloop, workface);
      if (workface.tet != nullptr) {
        // We must find a non-hull tet.
        if (ishulltet(workface)) {
          spintet = workface;
          while (1) {
            fnextself(spintet);
            if (!ishulltet(spintet)) break;
            if (spintet.tet == workface.tet) break;
          }
          assert(!ishulltet(spintet));
          workface = spintet;
        }
      }
    }
    if (b->order == 2) { // -o2
      // Get the extra vertex on this edge.
      if (workface.tet != nullptr) {
        extralist = (point *) workface.tet[highorderindex];
        pp = extralist[ver2edge[workface.ver]];
      } else {
        pp = torg; // There is no extra node available.
      }
    }
    if (b->neighout > 1) { // -nn
      if (workface.tet != nullptr) {
        neigh = elemindex(workface.tet);
      } else {
        neigh = -1;
      }
    }
    marker = shellmark(edgeloop);
    if (marker == 0) {
      marker = 1; // Default marker of a boundary edge is 1. 
    }
    if (out == (tetgenio *) nullptr) {
      fprintf(outfile, "%5d   %4d  %4d", edgenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift);
      if (b->order == 2) { // -o2
        fprintf(outfile, "  %4d", pointmark(pp) - shift);
      }
      fprintf(outfile, "  %d", marker);
      if (b->neighout > 1) { // -nn
        fprintf(outfile, "  %4d", neigh);
      }
      fprintf(outfile, "\n");
    } else {
      // Output three vertices of this face;
      elist[index++] = pointmark(torg) - shift;
      elist[index++] = pointmark(tdest) - shift;
      if (b->order == 2) { // -o2
        out->o2edgelist[o2index++] = pointmark(pp) - shift;
      }
      out->edgemarkerlist[i++] = marker;
      if (b->neighout > 1) { // -nn
        out->edgeadjtetlist[index2++] = neigh;
      }
    }
    edgenumber++;
    edgeloop.sh = shellfacetraverse(subsegs);
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outneighbors()    Output tet neighbors to a .neigh file or a structure.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outneighbors(tetgenio* out)
{
  FILE *outfile = nullptr;
  char neighborfilename[FILENAMESIZE];
  int *nlist = nullptr;
  int index = 0;
  triface tetloop, tetsym;
  int neighbori[4];
  int firstindex;
  int elementnumber;
  long ntets;

  if (out == (tetgenio *) nullptr) {
    strcpy(neighborfilename, b->outfilename);
    strcat(neighborfilename, ".neigh");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", neighborfilename);
    } else {
      printf("Writing neighbors.\n");
    }
  }

  ntets = tetrahedrons->items - hullsize;

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(neighborfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", neighborfilename);
      terminatetetgen(1);
    }
    // Number of tetrahedra, four faces per tetrahedron.
    fprintf(outfile, "%ld  %d\n", ntets, 4);
  } else {
    // Allocate memory for 'neighborlist'.
    out->neighborlist = new int[ntets * 4];
    if (out->neighborlist == (int *) nullptr) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    nlist = out->neighborlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) nullptr) {
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      fsym(tetloop, tetsym);
      if (!ishulltet(tetsym)) {
        neighbori[tetloop.ver] = elemindex(tetsym.tet);
      } else {
        neighbori[tetloop.ver] = -1;
      }
    }
    if (out == (tetgenio *) nullptr) {
      // Tetrahedra number, neighboring tetrahedron numbers.
      fprintf(outfile, "%4d    %4d  %4d  %4d  %4d\n", elementnumber,
              neighbori[0], neighbori[1], neighbori[2], neighbori[3]);
    } else {
      nlist[index++] = neighbori[0];
      nlist[index++] = neighbori[1];
      nlist[index++] = neighbori[2];
      nlist[index++] = neighbori[3];
    }
    tetloop.tet = tetrahedrontraverse();
    elementnumber++;
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outvoronoi()    Output the Voronoi diagram to .v.node, .v.edge, v.face,   //
//                 and .v.cell.                                              //
//                                                                           //
// The Voronoi diagram is the geometric dual of the Delaunay triangulation.  //
// The Voronoi vertices are the circumcenters of Delaunay tetrahedra.  Each  //
// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- //
// unay face. At a face of convex hull, it becomes a ray (goto the infinity).//
// A Voronoi face is the convex hull of all Voronoi vertices around a common //
// Delaunay edge. It is a closed polygon for any interal Delaunay edge. At a //
// ridge, it is unbounded.  Each Voronoi cell is the convex hull of all Vor- //
// onoi vertices around a common Delaunay vertex. It is a polytope for any   //
// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay    //
// vertex belonging to the convex hull.                                      //
//                                                                           //
// aunay tetrahedralization - the power diagram - of the weighted point set. //
// Note that the vertices of the power disgram are the centers of the ortho- //
// spheres of the tetrahedra.                                                //
//                                                                           //
// NOTE: This routine is only used when the input is only a set of point.    //
//                                                                           //
// Comment: Special thanks to Victor Liu for finding and fixing few bugs.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outvoronoi(tetgenio* out)
{
  FILE *outfile = nullptr;
  char outfilename[FILENAMESIZE];
  tetgenio::voroedge *vedge = nullptr;
  tetgenio::vorofacet *vfacet = nullptr;
  arraypool *tetlist, *ptlist;
  triface tetloop, worktet, spintet, firsttet;
  point pt[4], ploop, neipt;
  REAL ccent[3], infvec[3], vec1[3], vec2[3], L;
  long ntets, faces, edges;
  int *indexarray, *fidxs, *eidxs;
  int arraysize, *vertarray = nullptr;
  int vpointcount, vedgecount, vfacecount, tcount;
  int ishullvert, ishullface;
  int index, shift, end1, end2;
  int i, j;

  int t1ver; // used by fsymself()

  // Output Voronoi vertices to .v.node file.
  if (out == (tetgenio *) nullptr) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.node");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi vertices.\n");
    }
  }

  // Determine the first index (0 or 1).
  shift = (b->zeroindex ? 0 : in->firstnumber);

  // Each face and edge of the tetrahedral mesh will be indexed for indexing
  //   the Voronoi edges and facets. Indices of faces and edges are saved in
  //   each tetrahedron (including hull tets).

  // Allocate the total space once.
  indexarray = new int[tetrahedrons->items * 10];

  // Allocate space (10 integers) into each tetrahedron. It re-uses the slot
  //   for element markers, flags.
  i = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = alltetrahedrontraverse();
  while (tetloop.tet != nullptr) {
    tetloop.tet[11] = (tetrahedron) &(indexarray[i * 10]);
    i++;
    tetloop.tet = alltetrahedrontraverse();
  }

  // The number of tetrahedra (excluding hull tets) (Voronoi vertices).
  ntets = tetrahedrons->items - hullsize;
  // The number of Delaunay faces (Voronoi edges).
  faces = (4l * ntets + hullsize) / 2l;
  // The number of Delaunay edges (Voronoi faces).
  long vsize = points->items - dupverts - unuverts;
  if (b->weighted) vsize -= nonregularcount;
  edges = vsize + faces - ntets - 1;

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of voronoi points, 3 dim, no attributes, no marker.
    fprintf(outfile, "%ld  3  0  0\n", ntets);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofvpoints = (int) ntets;
    out->vpointlist = new REAL[out->numberofvpoints * 3];
    if (out->vpointlist == (REAL *) nullptr) {
      terminatetetgen(1);
    }
  }

  // Output Voronoi vertices (the circumcenters of tetrahedra). 
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vpointcount = 0; // The (internal) v-index always starts from 0. 
  index = 0;
  while (tetloop.tet != (tetrahedron *) nullptr) {
    for (i = 0; i < 4; i++) {
      pt[i] = (point) tetloop.tet[4 + i];
      setpoint2tet(pt[i], encode(tetloop));
    }
    if (b->weighted) {
      orthosphere(pt[0], pt[1], pt[2], pt[3], pt[0][3], pt[1][3], pt[2][3], 
                  pt[3][3], ccent, nullptr);
    } else {
      circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, nullptr);
    }
    if (out == (tetgenio *) nullptr) {
      fprintf(outfile, "%4d  %16.8e %16.8e %16.8e\n", vpointcount + shift,
              ccent[0], ccent[1], ccent[2]);
    } else {
      out->vpointlist[index++] = ccent[0];
      out->vpointlist[index++] = ccent[1];
      out->vpointlist[index++] = ccent[2];
    }
    setelemindex(tetloop.tet, vpointcount);
    vpointcount++;
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi edges to .v.edge file.
  if (out == (tetgenio *) nullptr) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.edge");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi edges.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi edges, no marker.
    fprintf(outfile, "%ld  0\n", faces);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofvedges = (int) faces;
    out->vedgelist = new tetgenio::voroedge[out->numberofvedges];
  }

  // Output the Voronoi edges. 
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vedgecount = 0; // D-Face (V-edge) index (from zero).
  index = 0; // The Delaunay-face index.
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Count the number of Voronoi edges. Look at the four faces of each
    //   tetrahedron. Count the face if the tetrahedron's index is
    //   smaller than its neighbor's or the neighbor is outside.
    end1 = elemindex(tetloop.tet);
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      fsym(tetloop, worktet);
      if (ishulltet(worktet) || 
          (elemindex(tetloop.tet) < elemindex(worktet.tet))) {
        // Found a Voronoi edge. Operate on it.
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, "%4d  %4d", vedgecount + shift, end1 + shift);
        } else {
          vedge = &(out->vedgelist[index++]);
          vedge->v1 = end1 + shift;
        }
        if (!ishulltet(worktet)) {
          end2 = elemindex(worktet.tet);
        } else {
          end2 = -1;
        }
        // Note that end2 may be -1 (worktet.tet is outside).
        if (end2 == -1) {
          // Calculate the out normal of this hull face.
          pt[0] = dest(worktet);
          pt[1] = org(worktet);
          pt[2] = apex(worktet);
          for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j];
          for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j];
          cross(vec1, vec2, infvec);
          // Normalize it.
          L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1]
                   + infvec[2] * infvec[2]);
          if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L;
          if (out == (tetgenio *) nullptr) {
            fprintf(outfile, " -1");
            fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]);
          } else {
            vedge->v2 = -1;
            vedge->vnormal[0] = infvec[0];
            vedge->vnormal[1] = infvec[1];
            vedge->vnormal[2] = infvec[2];
          }
        } else {
          if (out == (tetgenio *) nullptr) {
            fprintf(outfile, " %4d\n", end2 + shift);
          } else {
            vedge->v2 = end2 + shift;
            vedge->vnormal[0] = 0.0;
            vedge->vnormal[1] = 0.0;
            vedge->vnormal[2] = 0.0;
          }
        }
        // Save the V-edge index in this tet and its neighbor.
        fidxs = (int *) (tetloop.tet[11]);
        fidxs[tetloop.ver] = vedgecount;
        fidxs = (int *) (worktet.tet[11]);
        fidxs[worktet.ver & 3] = vedgecount;
        vedgecount++;
      }
    } // tetloop.ver
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi faces to .v.face file.
  if (out == (tetgenio *) nullptr) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.face");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi faces.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi faces.
    fprintf(outfile, "%ld  0\n", edges);
  } else {
    out->numberofvfacets = edges;
    out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets];
    if (out->vfacetlist == (tetgenio::vorofacet *) nullptr) {
      terminatetetgen(1);
    }
  }

  // Output the Voronoi facets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vfacecount = 0; // D-edge (V-facet) index (from zero).
  while (tetloop.tet != (tetrahedron *) nullptr) {
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's index is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      worktet.ver = edge2ver[i];
      // Count the number of faces at this edge. If the edge is a hull edge,
      //   the face containing dummypoint is also counted.
      //ishulledge = 0; // Is it a hull edge.
      tcount = 0;
      firsttet = worktet;
      spintet = worktet;
      while (1) {
        tcount++;
        fnextself(spintet);
        if (spintet.tet == worktet.tet) break;
        if (!ishulltet(spintet)) {
          if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
        } else {
          //ishulledge = 1;
          if (apex(spintet) == dummypoint) {
            // We make this V-edge appear in the end of the edge list.
            fnext(spintet, firsttet); 
          }
        }
      } // while (1)
      if (spintet.tet == worktet.tet) {
        // Found a Voronoi facet. Operate on it.
        pt[0] = org(worktet);
        pt[1] = dest(worktet);
        end1 = pointmark(pt[0]) - in->firstnumber; // V-cell index
        end2 = pointmark(pt[1]) - in->firstnumber;
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, "%4d  %4d %4d  %-2d ", vfacecount + shift, 
                  end1 + shift, end2 + shift, tcount);
        } else {
          vfacet = &(out->vfacetlist[vfacecount]);
          vfacet->c1 = end1 + shift;
          vfacet->c2 = end2 + shift;
          vfacet->elist = new int[tcount + 1];
          vfacet->elist[0] = tcount;
          index = 1;
        }
        // Output V-edges of this V-facet.
        spintet = firsttet; //worktet;
        while (1) {
          fidxs = (int *) (spintet.tet[11]);
          if (apex(spintet) != dummypoint) {
            vedgecount = fidxs[spintet.ver & 3];
            ishullface = 0;
          } else {
            ishullface = 1; // It's not a real face.
          }
          if (out == (tetgenio *) nullptr) {
            fprintf(outfile, " %d", !ishullface ? (vedgecount + shift) : -1); 
          } else {
            vfacet->elist[index++] = !ishullface ? (vedgecount + shift) : -1;
          }
          // Save the V-facet index in this tet at this edge.
          eidxs = &(fidxs[4]);
          eidxs[ver2edge[spintet.ver]] = vfacecount;
          // Go to the next face.
          fnextself(spintet);
          if (spintet.tet == firsttet.tet) break;
        } // while (1)
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, "\n");
        }
        vfacecount++;
      } // if (spintet.tet == worktet.tet)
    } // if (i = 0; i < 6; i++)
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi cells to .v.cell file.
  if (out == (tetgenio *) nullptr) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.cell");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) nullptr) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi cells.\n");
    }
  }

  if (out == (tetgenio *) nullptr) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) nullptr) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi cells.
    fprintf(outfile, "%ld\n", points->items - unuverts - dupverts);
  } else {
    out->numberofvcells = points->items - unuverts - dupverts;
    out->vcelllist = new int*[out->numberofvcells];
    if (out->vcelllist == (int **) nullptr) {
      terminatetetgen(1);
    }
  }

  // Output Voronoi cells.
  tetlist = cavetetlist;
  ptlist = cavetetvertlist;
  points->traversalinit();
  ploop = pointtraverse();
  vpointcount = 0;
  while (ploop != (point) nullptr) {
    if ((pointtype(ploop) != UNUSEDVERTEX) &&
        (pointtype(ploop) != DUPLICATEDVERTEX) &&
        (pointtype(ploop) != NREGULARVERTEX)) { 
      getvertexstar(1, ploop, tetlist, ptlist, nullptr);
      // Mark all vertices. Check if it is a hull vertex.
      ishullvert = 0;
      for (i = 0; i < ptlist->objects; i++) {
        neipt = * (point *) fastlookup(ptlist, i);
        if (neipt != dummypoint) {
          pinfect(neipt);
        } else {
          ishullvert = 1;
        }
      }
      tcount = (int) ptlist->objects;
      if (out == (tetgenio *) nullptr) {
        fprintf(outfile, "%4d  %-2d ", vpointcount + shift, tcount);
      } else {
        arraysize = tcount;
        vertarray = new int[arraysize + 1];
        out->vcelllist[vpointcount] = vertarray;
        vertarray[0] = tcount;
        index = 1;
      }
      // List Voronoi facets bounding this cell.
      for (i = 0; i < tetlist->objects; i++) {
        worktet = * (triface *) fastlookup(tetlist, i);
        // Let 'worktet' be [a,b,c,d] where d = ploop.
        for (j = 0; j < 3; j++) {
          neipt = org(worktet); // neipt is a, or b, or c
          // Skip the dummypoint.
          if (neipt != dummypoint) {
            if (pinfected(neipt)) {
              // It's not processed yet.
              puninfect(neipt);
              // Go to the DT edge [a,d], or [b,d], or [c,d]. 
              esym(worktet, spintet);
              enextself(spintet);
              // Get the V-face dual to this edge.
              eidxs = (int *) spintet.tet[11];
              vfacecount = eidxs[4 + ver2edge[spintet.ver]];
              if (out == (tetgenio *) nullptr) {
                fprintf(outfile, " %d", vfacecount + shift);
              } else {
                vertarray[index++] = vfacecount + shift;
              }
            }
          }
          enextself(worktet);
        } // j
      } // i
      if (ishullvert) {
        // Add a hull facet (-1) to the facet list.
        if (out == (tetgenio *) nullptr) {
          fprintf(outfile, " -1");
        } else {
          vertarray[index++] = -1;
        }
      }
      if (out == (tetgenio *) nullptr) {
        fprintf(outfile, "\n");
      }
      tetlist->restart();
      ptlist->restart();
      vpointcount++;
    }
    ploop = pointtraverse();
  }

  // Delete the space for face/edge indices.
  delete [] indexarray;

  if (out == (tetgenio *) nullptr) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsmesh()    Write surface mesh to a .smesh file, which can be read and  //
//               tetrahedralized by TetGen.                                  //
//                                                                           //
// You can specify a filename (without suffix) in 'smfilename'. If you don't //
// supply a filename (let smfilename be nullptr), the default name stored in    //
// 'tetgenbehavior' will be used.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsmesh(char* smfilename)
{
  FILE *outfile;
  char nodfilename[FILENAMESIZE];
  char smefilename[FILENAMESIZE];
  face faceloop;
  point p1, p2, p3;
  int firstindex, shift;
  int bmark;
  int faceid, marker;
  int i;

  if (smfilename != (char *) nullptr && smfilename[0] != '\0') {
    strcpy(smefilename, smfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(smefilename, b->outfilename);
  } else {
    strcpy(smefilename, "unnamed");
  }
  strcpy(nodfilename, smefilename);
  strcat(smefilename, ".smesh");
  strcat(nodfilename, ".node");

  if (!b->quiet) {
    printf("Writing %s.\n", smefilename);
  }
  outfile = fopen(smefilename, "w");
  if (outfile == (FILE *) nullptr) {
    printf("File I/O Error:  Cannot create file %s.\n", smefilename);
    return;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  fprintf(outfile, "# %s.  TetGen's input file.\n", smefilename);
  fprintf(outfile, "\n# part 1: node list.\n");
  fprintf(outfile, "0  3  0  0  # nodes are found in %s.\n", nodfilename);

  marker = 0; // avoid compile warning.
  bmark = !b->nobound && in->facetmarkerlist;  

  fprintf(outfile, "\n# part 2: facet list.\n");
  // Number of facets, boundary marker.
  fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
  
  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  while (faceloop.sh != (shellface *) nullptr) {
    p1 = sorg(faceloop);
    p2 = sdest(faceloop);
    p3 = sapex(faceloop);
    if (bmark) {
      faceid = shellmark(faceloop) - 1;
      if (faceid >= 0) { 
        marker = in->facetmarkerlist[faceid];
      } else {
        marker = 0; // This subface must be added manually later.
      }
    }
    fprintf(outfile, "3    %4d  %4d  %4d", pointmark(p1) - shift,
            pointmark(p2) - shift, pointmark(p3) - shift);
    if (bmark) {
      fprintf(outfile, "    %d", marker);
    }
    fprintf(outfile, "\n");
    faceloop.sh = shellfacetraverse(subfaces);
  }

  // Copy input holelist.
  fprintf(outfile, "\n# part 3: hole list.\n");
  fprintf(outfile, "%d\n", in->numberofholes);
  for (i = 0; i < in->numberofholes; i++) {
    fprintf(outfile, "%d  %g  %g  %g\n", i + in->firstnumber,
            in->holelist[i * 3], in->holelist[i * 3 + 1],
            in->holelist[i * 3 + 2]);
  }

  // Copy input regionlist.
  fprintf(outfile, "\n# part 4: region list.\n");
  fprintf(outfile, "%d\n", in->numberofregions);
  for (i = 0; i < in->numberofregions; i++) {
    fprintf(outfile, "%d  %g  %g  %g  %d  %g\n", i + in->firstnumber,
            in->regionlist[i * 5], in->regionlist[i * 5 + 1],
            in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3],
            in->regionlist[i * 5 + 4]);
  }

  fprintf(outfile, "# Generated by %s\n", b->commandline);
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2medit()    Write mesh to a .mesh file, which can be read and      //
//                    rendered by Medit (a free mesh viewer from INRIA).     //
//                                                                           //
// You can specify a filename (without suffix) in 'mfilename'.  If you don't //
// supply a filename (let mfilename be nullptr), the default name stored in     //
// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2medit(char* mfilename)
{
  FILE *outfile;
  char mefilename[FILENAMESIZE];
  tetrahedron* tetptr;
  triface tface, tsymface;
  face segloop, checkmark;
  point ptloop, p1, p2, p3, p4;
  long ntets, faces;
  int pointnumber;
  int faceid, marker;
  int i;

  if (mfilename != (char *) nullptr && mfilename[0] != '\0') {
    strcpy(mefilename, mfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(mefilename, b->outfilename);
  } else {
    strcpy(mefilename, "unnamed");
  }
  strcat(mefilename, ".mesh");

  if (!b->quiet) {
    printf("Writing %s.\n", mefilename);
  }
  outfile = fopen(mefilename, "w");
  if (outfile == (FILE *) nullptr) {
    printf("File I/O Error:  Cannot create file %s.\n", mefilename);
    return;
  }

  fprintf(outfile, "MeshVersionFormatted 1\n");
  fprintf(outfile, "\n");
  fprintf(outfile, "Dimension\n");
  fprintf(outfile, "3\n");
  fprintf(outfile, "\n");

  fprintf(outfile, "\n# Set of mesh vertices\n");
  fprintf(outfile, "Vertices\n");
  fprintf(outfile, "%ld\n", points->items);

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Medit need start number form 1.
  while (ptloop != (point) nullptr) {
    // Point coordinates.
    fprintf(outfile, "%.17g  %.17g  %.17g", ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g\n", ptloop[3]);
    } else {
      fprintf(outfile, "    0\n");
    }
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  // Compute the number of faces.
  ntets = tetrahedrons->items - hullsize;
  faces = (ntets * 4l + hullsize) / 2l;

  fprintf(outfile, "\n# Set of Triangles\n");
  fprintf(outfile, "Triangles\n");
  fprintf(outfile, "%ld\n", faces);

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  while (tface.tet != (tetrahedron *) nullptr) {
    for (tface.ver = 0; tface.ver < 4; tface.ver ++) {
      fsym(tface, tsymface);
      if (ishulltet(tsymface) || 
          (elemindex(tface.tet) < elemindex(tsymface.tet))) {
        p1 = org (tface);
        p2 = dest(tface);
        p3 = apex(tface);
        fprintf(outfile, "%5d  %5d  %5d",
                pointmark(p1), pointmark(p2), pointmark(p3));
        // Check if it is a subface.
        tspivot(tface, checkmark);
        if (checkmark.sh == nullptr) {
          marker = 0;  // It is an inner face. It's marker is 0.
        } else {
          if (in->facetmarkerlist) {
            // The facet marker is given, get it.
            faceid = shellmark(checkmark) - 1;
            marker = in->facetmarkerlist[faceid];
          } else {
            marker = 1; // The default marker for subface is 1.
          }
        }
        fprintf(outfile, "    %d\n", marker);
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "\n# Set of Tetrahedra\n");
  fprintf(outfile, "Tetrahedra\n");
  fprintf(outfile, "%ld\n", ntets);

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  while (tetptr != (tetrahedron *) nullptr) {
    if (!b->reversetetori) {
      p1 = (point) tetptr[4];
      p2 = (point) tetptr[5];
    } else {
      p1 = (point) tetptr[5];
      p2 = (point) tetptr[4];
    }
    p3 = (point) tetptr[6];
    p4 = (point) tetptr[7];
    fprintf(outfile, "%5d  %5d  %5d  %5d",
            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
    if (numelemattrib > 0) {
      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
    } else {
      fprintf(outfile, "  0");
    }
    fprintf(outfile, "\n");
    tetptr = tetrahedrontraverse();
  }

  fprintf(outfile, "\nCorners\n");
  fprintf(outfile, "%d\n", in->numberofpoints);

  for (i = 0; i < in->numberofpoints; i++) {
    fprintf(outfile, "%4d\n", i + 1);
  }

  if (b->plc || b->refine) {
    fprintf(outfile, "\nEdges\n");
    fprintf(outfile, "%ld\n", subsegs->items);

    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != (shellface *) nullptr) {
      p1 = sorg(segloop);
      p2 = sdest(segloop);
      fprintf(outfile, "%5d  %5d", pointmark(p1), pointmark(p2));
      marker = shellmark(segloop);
      fprintf(outfile, "    %d\n", marker);
      segloop.sh = shellfacetraverse(subsegs);
    }
  }

  fprintf(outfile, "\nEnd\n");
  fclose(outfile);
}



///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2vtk()    Save mesh to file in VTK Legacy format.                  //
//                                                                           //
// This function was contributed by Bryn Llyod from ETH, 2007.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2vtk(char* ofilename)
{
  FILE *outfile;
  char vtkfilename[FILENAMESIZE];
  point pointloop, p1, p2, p3, p4;
  tetrahedron* tptr;
  double x, y, z;
  int n1, n2, n3, n4;
  int nnodes = 4;
  int celltype = 10;

  int NEL = tetrahedrons->items - hullsize;
  int NN = points->items;

  if (ofilename != (char *) nullptr && ofilename[0] != '\0') {
    strcpy(vtkfilename, ofilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(vtkfilename, b->outfilename);
  } else {
    strcpy(vtkfilename, "unnamed");
  }
  strcat(vtkfilename, ".vtk");

  if (!b->quiet) {
    printf("Writing %s.\n", vtkfilename);
  }
  outfile = fopen(vtkfilename, "w");
  if (outfile == (FILE *) nullptr) {
    printf("File I/O Error:  Cannot create file %s.\n", vtkfilename);
    return;
  }

  //always write big endian
  //bool ImALittleEndian = !testIsBigEndian();

  fprintf(outfile, "# vtk DataFile Version 2.0\n");
  fprintf(outfile, "Unstructured Grid\n");
  fprintf(outfile, "ASCII\n"); // BINARY
  fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n");
  fprintf(outfile, "POINTS %d double\n", NN);

  points->traversalinit();
  pointloop = pointtraverse();
  for(int id=0; id<NN && pointloop != (point) nullptr; id++){
    x = pointloop[0];
    y = pointloop[1];
    z = pointloop[2];
    fprintf(outfile, "%.17g %.17g %.17g\n", x, y, z);
    pointloop = pointtraverse();
  }
  fprintf(outfile, "\n");

  fprintf(outfile, "CELLS %d %d\n", NEL, NEL*(4+1));
  //NEL rows, each has 1 type id + 4 node id's
 
  tetrahedrons->traversalinit();
  tptr = tetrahedrontraverse();
  //elementnumber = firstindex; // in->firstnumber;
  if (b->order == 2) {
    printf("  Write VTK not implemented for order 2 elements \n");
    return;
  }
  while (tptr != (tetrahedron *) nullptr) {
    if (!b->reversetetori) {
      p1 = (point) tptr[4];
      p2 = (point) tptr[5];
    } else {
      p1 = (point) tptr[5];
      p2 = (point) tptr[4];
    }
    p3 = (point) tptr[6];
    p4 = (point) tptr[7];
    n1 = pointmark(p1) - in->firstnumber;
    n2 = pointmark(p2) - in->firstnumber;
    n3 = pointmark(p3) - in->firstnumber;
    n4 = pointmark(p4) - in->firstnumber;
    fprintf(outfile, "%d  %4d %4d %4d %4d\n", nnodes, n1, n2, n3, n4);
    tptr = tetrahedrontraverse();
  }
  fprintf(outfile, "\n");

  fprintf(outfile, "CELL_TYPES %d\n", NEL);
  for(int tid=0; tid<NEL; tid++){
    fprintf(outfile, "%d\n", celltype);
  }
  fprintf(outfile, "\n");

  fclose(outfile);
}

////                                                                       ////
////                                                                       ////
//// output_cxx ///////////////////////////////////////////////////////////////

//// main_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedralize()    The interface for users using TetGen library to       //
//                     generate tetrahedral meshes with all features.        //
//                                                                           //
// The sequence is roughly as follows.  Many of these steps can be skipped,  //
// depending on the command line switches.                                   //
//                                                                           //
// - Initialize constants and parse the command line.                        //
// - Read the vertices from a file and either                                //
//   - tetrahedralize them (no -r), or                                       //
//   - read an old mesh from files and reconstruct it (-r).                  //
// - Insert the boundary segments and facets (-p or -Y).                     //
// - Read the holes (-p), regional attributes (-pA), and regional volume     //
//   constraints (-pa).  Carve the holes and concavities, and spread the     //
//   regional attributes and volume constraints.                             //
// - Enforce the constraints on minimum quality bound (-q) and maximum       //
//   volume (-a), and a mesh size function (-m).                             //
// - Optimize the mesh wrt. specified quality measures (-O and -o).          //
// - Write the output files and print the statistics.                        //
// - Check the consistency of the mesh (-C).                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out,
                    tetgenio *addin, tetgenio *bgmin)
{
  tetgenmesh m;
  clock_t tv[10], ts[5]; // Timing informations (defined in time.h)
  REAL cps = (REAL) CLOCKS_PER_SEC;

  tv[0] = clock();
 
  m.b = b;
  m.in = in;
  m.addin = addin;

  if ((b->metric) && (bgmin->numberofpoints > 0)) {
    m.bgm = new tetgenmesh(); // Create an empty background mesh.
    m.bgm->b = b;
    m.bgm->in = bgmin;
  }

  m.initializepools();
  m.transfernodes();

   
  exactinit() ; // [bruno] 
/*   
  exactinit(b->verbose, b->noexact, b->nostaticfilter,
            m.xmax - m.xmin, m.ymax - m.ymin, m.zmax - m.zmin);
*/
   
  tv[1] = clock();

  if (b->refine) {
    m.reconstructmesh();
  } else { // b->plc
    m.incrementaldelaunay(ts[0]);
  }

  tv[2] = clock();

  if (!b->quiet) {
    if (b->refine) {
      printf("Mesh reconstruction seconds:  %g\n", ((REAL)(tv[2]-tv[1])) / cps);
    } else {
      printf("Delaunay seconds:  %g\n", ((REAL)(tv[2]-tv[1])) / cps);
      if (b->verbose) {
        printf("  Point sorting seconds:  %g\n", ((REAL)(ts[0]-tv[1])) / cps);
      }
    }
  }

  if (b->plc) { // -p
    m.meshsurface();

    ts[0] = clock();

    if (!b->quiet) {
      printf("Surface mesh seconds:  %g\n", ((REAL)(ts[0]-tv[2])) / cps);
    }

    if (b->diagnose) { // -d
      m.detectinterfaces();

      ts[1] = clock();

      if (!b->quiet) {
        printf("Self-intersection seconds:  %g\n", ((REAL)(ts[1]-ts[0])) / cps);
      }

      // Only output when self-intersecting faces exist.
      if (m.subfaces->items > 0l) {
        m.outnodes(out);
        m.outsubfaces(out);
      }

      return;
    }
  }

  tv[3] = clock();

  if ((b->metric) && (m.bgm != nullptr)) { // -m
    m.bgm->initializepools();
    m.bgm->transfernodes();
    m.bgm->reconstructmesh();

    ts[0] = clock();

    if (!b->quiet) {
      printf("Background mesh reconstruct seconds:  %g\n",
             ((REAL)(ts[0] - tv[3])) / cps);
    }

    if (b->metric) { // -m
      m.interpolatemeshsize();

      ts[1] = clock();

      if (!b->quiet) {
        printf("Size interpolating seconds:  %g\n",((REAL)(ts[1]-ts[0])) / cps);
      }
    }
  }

  tv[4] = clock();

  if (b->plc) { // -p
    if (b->nobisect) { // -Y
      m.recoverboundary(ts[0]);
    } else {
      m.constraineddelaunay(ts[0]);
    }

    ts[1] = clock();

    if (!b->quiet) {
      printf("Boundary recovery seconds:  %g\n", ((REAL)(ts[1]-tv[4])) / cps);
      if (b->verbose) {
        printf("  Segment recovery seconds:  %g\n",((REAL)(ts[0]-tv[4]))/ cps);
        printf("  Facet recovery seconds:  %g\n", ((REAL)(ts[1]-ts[0])) / cps);
      }
    }

    m.carveholes();

    ts[2] = clock();

    if (!b->quiet) {
      printf("Exterior tets removal seconds:  %g\n", 
             ((REAL)(ts[2]-ts[1])) / cps);
    }

    if (b->nobisect) { // -Y
      if (m.subvertstack->objects > 0l) {
        m.suppresssteinerpoints();

        ts[3] = clock();

        if (!b->quiet) {
          printf("Steiner suppression seconds:  %g\n",((REAL)(ts[3]-ts[2]))/cps);
        }
      } else {
        ts[3] = clock();
      }

      m.recoverdelaunay(); 

      ts[4] = clock();

      if (!b->quiet) {
        printf("Delaunay recovery seconds:  %g\n", ((REAL)(ts[4]-ts[3]))/cps);
      }
    }
  }

  tv[5] = clock();

  if ((b->plc || b->refine) && b->insertaddpoints) { // -i
    if ((addin != nullptr) && (addin->numberofpoints > 0)) {
      m.insertconstrainedpoints(addin); 
    }
  }

  tv[6] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && b->insertaddpoints) {
      if ((addin != nullptr) && (addin->numberofpoints > 0)) {
        printf("Constrained points seconds:  %g\n", ((REAL)(tv[6]-tv[5]))/cps);
      }
    }
  }


  if (b->quality) {
      m.delaunayrefinement();
  }

  tv[7] = clock();

  if (!b->quiet) {
    if (b->quality) {
      printf("Refinement seconds:  %g\n", ((REAL)(tv[7] - tv[6])) / cps);
    }
  }

  if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) {
    m.optimizemesh();
  }

  tv[8] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && (b->optlevel > 0) && !b->conforming) {
      printf("Optimization seconds:  %g\n", ((REAL)(tv[8] - tv[7])) / cps);
    }
  }

  if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0)
      || (b->refine && (in->numberofcorners == 10)))) {
    m.jettisonnodes();
  }

  if ((b->order == 2) && !b->convex) {
    m.highorder();
  }

  if (!b->quiet) {
    printf("\n");
  }

  if (out != (tetgenio *) nullptr) {
    out->firstnumber = in->firstnumber;
    out->mesh_dim = in->mesh_dim;
  }

  if (b->nonodewritten || b->noiterationnum) {
    if (!b->quiet) {
      printf("NOT writing a .node file.\n");
    }
  } else {
    m.outnodes(out);
  }

  if (b->noelewritten) {
    if (!b->quiet) {
      printf("NOT writing an .ele file.\n");
    }
  } else {
    if (m.tetrahedrons->items > 0l) {
      m.outelements(out);
    }
  }

  if (b->nofacewritten) {
    if (!b->quiet) {
      printf("NOT writing an .face file.\n");
    }
  } else {
    if (b->facesout) {
      if (m.tetrahedrons->items > 0l) {
        m.outfaces(out);  // Output all faces.
      }
    } else {
      if (b->plc || b->refine) {
        if (m.subfaces->items > 0l) {
          m.outsubfaces(out); // Output boundary faces.
        }
      } else {
        if (m.tetrahedrons->items > 0l) {
          m.outhullfaces(out); // Output convex hull faces.
        }
      }
    }
  }


  if (b->nofacewritten) {
    if (!b->quiet) {
      printf("NOT writing an .edge file.\n");
    }
  } else {
    if (b->edgesout) { // -e
      m.outedges(out); // output all mesh edges. 
    } else {
      if (b->plc || b->refine) {
        m.outsubsegments(out); // output subsegments.
      }
    }
  }

  if ((b->plc || b->refine) && b->metric) { // -m
    m.outmetrics(out);
  }

  if (!out && b->plc && 
      ((b->object == tetgenbehavior::OFF) ||
       (b->object == tetgenbehavior::PLY) ||
       (b->object == tetgenbehavior::STL))) {
    m.outsmesh(b->outfilename);
  }

  if (!out && b->meditview) {
    m.outmesh2medit(b->outfilename); 
  }


  if (!out && b->vtkview) {
    m.outmesh2vtk(b->outfilename); 
  }

  if (b->neighout) {
    m.outneighbors(out);
  }

  if ((!(b->plc || b->refine)) && b->voroout) {
    m.outvoronoi(out);
  }


  tv[9] = clock();

  if (!b->quiet) {
    printf("\nOutput seconds:  %g\n", ((REAL)(tv[9] - tv[8])) / cps);
    printf("Total running seconds:  %g\n", ((REAL)(tv[9] - tv[0])) / cps);
  }

  if (b->docheck) {
    m.checkmesh(0);
    if (b->plc || b->refine) {
      m.checkshells();
      m.checksegments();
    }
    if (b->docheck > 1) {
      m.checkdelaunay();
    }
  }

  if (!b->quiet) {
    m.statistics();
  }
}

#ifndef TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// main()    The entrance for running TetGen from command line.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])

#else // with TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedralize()    The entrance for calling TetGen from another program. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, 
                    tetgenio *addin, tetgenio *bgmin)

#endif // not TETLIBRARY

{
  tetgenbehavior b;

#ifndef TETLIBRARY

  tetgenio in, addin, bgmin;
  
  if (!b.parse_commandline(argc, argv)) {
    terminatetetgen(10);
  }

  // Read input files.
  if (b.refine) { // -r
    if (!in.load_tetmesh(b.infilename, (int) b.object)) {
      terminatetetgen(10);
    }
  } else { // -p
    if (!in.load_plc(b.infilename, (int) b.object)) {
      terminatetetgen(10);
    }
  }
  if (b.insertaddpoints) { // -i
    // Try to read a .a.node file.
    addin.load_node(b.addinfilename);
  }
  if (b.metric) { // -m
    // Try to read a background mesh in files .b.node, .b.ele.
    bgmin.load_tetmesh(b.bgmeshfilename, (int) b.object);
  }

  tetrahedralize(&b, &in, nullptr, &addin, &bgmin);

  return 0;

#else // with TETLIBRARY

  if (!b.parse_commandline(switches)) {
    terminatetetgen(10);
  }
  tetrahedralize(&b, in, out, addin, bgmin);

#endif // not TETLIBRARY
}

////                                                                       ////
////                                                                       ////
//// main_cxx /////////////////////////////////////////////////////////////////

} // [Bruno] namespace GEO_3rdParty    
