//============================================================================//
//                                                                            //
// TetGen                                                                     //
//                                                                            //
// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator        //
//                                                                            //
// Version 1.6.0                                                              //
// August 31, 2020                                                            //
//                                                                            //
// Copyright (C) 2002--2020                                                   //
//                                                                            //
// 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 a tetrahedral mesh generator. It creates 3d triangulations of    //
//   polyhedral domains. It generates meshes with well-shaped elements whose  //
//   sizes are adapted to the geometric features or user-provided sizing      //
//   functions. It has applications in various applications in scientific     //
//   computing, such as computer graphics (CG), computer-aided design (CAD),  //
//   geometry processing (parametrizations and computer animation), and       //
//   physical simulations (finite element analysis).                          //
//                                                                            //
// TetGen computes (weighted) Delaunay triangulations for three-dimensional   //
//   (weighted) point sets, and constrained Delaunay triangulations for       //
//   three-dimensional polyhedral domains. In the latter case, input edges    //
//   and triangles can be completely preserved in the output meshes.  TetGen  //
//   can refine or coarsen an existing mesh to result in good quality and     //
//   size-adapted mesh according to the geometric features and user-defined   //
//   mesh sizing functions.                                                   //
//                                                                            //
// TetGen implements theoretically proven algorithms for computing the        //
//   Delaunay and constrained Delaunay tetrahedralizations. TetGen achieves   //
//   robustness and efficiency by using advanced techniques in computational  //
//   geometry. A technical paper describes the algorithms and methods         //
//   implemented in TetGen is available in ACM-TOMS, Hang Si ``TetGen, a      //
//   Delaunay-Based Quality Tetrahedral Mesh Generator", ACM Transactions on  //
//   Mathematical Software, February 2015, https://doi.org/10.1145/2629697.   //
//                                                                            //
// 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"

//== 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 *) NULL) {
    terminatetetgen(NULL, 1);
  }
  if (numberofpointattributes > 0) {
    pointattributelist = new REAL[numberofpoints * numberofpointattributes];
    if (pointattributelist == (REAL *) NULL) {
      terminatetetgen(NULL, 1);
    }
  }
  if (markers) {
    pointmarkerlist = new int[numberofpoints];
    if (pointmarkerlist == (int *) NULL) {
      terminatetetgen(NULL, 1);
    }
  }
  if (uvflag) {
    pointparamlist = new pointparam[numberofpoints];
    if (pointparamlist == NULL) {
      terminatetetgen(NULL, 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 (i < numberofpoints) {
    // Failed to read points due to some error.
    delete [] pointlist;
    pointlist = (REAL *) NULL;
    if (markers) {
      delete [] pointmarkerlist;
      pointmarkerlist = (int *) NULL;
    }
    if (numberofpointattributes > 0) {
      delete [] pointattributelist;
      pointattributelist = (REAL *) NULL;
    }
    if (uvflag) {
      delete [] pointparamlist;
      pointparamlist = NULL;
    }
    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 *) NULL) {
    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 (required by a PSC). 

  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, innodefilename);
  // Does this file contain an index column?
  stringptr = strstr(inputline, "rbox");
  if (stringptr == NULL) {
    // 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 *) NULL) {
    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 *) NULL) {
      terminatetetgen(NULL, 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(NULL, 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(NULL, 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 *) NULL) {
    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 *) NULL) {
      terminatetetgen(NULL, 1);
    }
    if (markers) {
      trifacemarkerlist = new int[numberoftrifaces];
      if (trifacemarkerlist == (int *) NULL) {
        terminatetetgen(NULL, 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(NULL, 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(NULL, 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 *) NULL) {
    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 *) NULL) {
    terminatetetgen(NULL, 1);
  }
  // Allocate memory for output tetrahedron attributes if necessary.
  if (numberoftetrahedronattributes > 0) {
    tetrahedronattributelist = new REAL[numberoftetrahedra *
                                        numberoftetrahedronattributes];
    if (tetrahedronattributelist == (REAL *) NULL) {
      terminatetetgen(NULL, 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(NULL, 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(NULL, 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 *) NULL) {
    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 *) NULL) {
    terminatetetgen(NULL, 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 *) NULL) {
    printf("Opening %s.\n", varfilename);
  } else {
    return false;
  }

  // Read the facet constraint section.
  stringptr = readnumberline(inputline, infile, varfilename);
  if (stringptr == NULL) {
    // No region list, return.
    fclose(infile);
    return true;
  }
  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 == NULL) {
    // No segment list, return.
    fclose(infile);
    return true;
  }
  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 *) NULL) {
    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 != 1) && (numberofpointmtrs != 3) &&
      (numberofpointmtrs != 6)) {
    // Column number doesn't match. 
    numberofpointmtrs = 0;
    printf("  !! Metric size does not match (1, 3, or 6). Ignored.\n");
    fclose(infile);
    return false;
  }

  // Allocate space for pointmtrlist.
  pointmtrlist = new REAL[numberofpoints * numberofpointmtrs];
  if (pointmtrlist == (REAL *) NULL) {
    terminatetetgen(NULL, 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(NULL, 1);
      }
      mtr = (REAL) strtod(stringptr, &stringptr);
      pointmtrlist[mtrindex++] = mtr;
      stringptr = findnextnumber(stringptr);
    }
  }

  fclose(infile);
  return true;
}

//============================================================================//
//                                                                            //
// load_elem()    Load a list of refine elements from an .elem file.          //
//                                                                            //
//============================================================================//

bool tetgenio::load_elem(char* filebasename)
{
  FILE *infile;
  char inelemfilename[FILENAMESIZE];
  char line[1024];

  strcpy(inelemfilename, filebasename);
  strcat(inelemfilename, ".elem");

  // Try to open a .elem file.
  infile = fopen(inelemfilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", inelemfilename);
  } else {
    return false;
  }

  int elenum = 0;
  float growth_ratio = 0.;
  fgets(line, 1023, infile);
  sscanf(line, "%d %f", &elenum, &growth_ratio);

  if (elenum == 0) {
    fclose(infile);
    return false;
  }

  refine_elem_list = new int[elenum * 4];
  numberofrefineelems = elenum;
  
  int *idx, i;
  for (i = 0; i < elenum; i++) {
    fgets(line, 1023, infile);
    idx = &(refine_elem_list[i*4]);
    sscanf(line, "%d %d %d %d", &(idx[0]), &(idx[1]), &(idx[2]), &(idx[3]));
  }

  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 *) NULL) {
    // .poly doesn't exist! Try to open a .smesh file.
    infile = fopen(insmeshfilename, "r");
    if (infile == (FILE *) NULL) {
      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-dimensional coordinates.
  numberofpointattributes = 0;  // no point attribute.
  markers = 0;  // no boundary marker.
  uvflag = 0; // no uv parameters (required 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 == NULL) {
      // 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 == NULL) {
      // 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, NULL);
    if (stringptr != (char *) NULL && *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;
      }
    }

  }

  // 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)) != NULL) {
    // Check section
    if (nverts == 0) {
      // Read header 
      bufferp = strstr(bufferp, "OFF");
      if (bufferp != NULL) {
        // 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;
    }
  }

  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)) != NULL) {
    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;
    }
  }

  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.                    //
//                                                                            //
//============================================================================//

static void SwapBytes(char *array, int size, int n)
{
  char *x = new char[size];
  for(int i = 0; i < n; i++) {
    char *a = &array[i * size];
    memcpy(x, a, size);
    for(int c = 0; c < size; c++)
      a[size - 1 - c] = x[c];
  }
  delete [] x;
}

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, "rb"))) {
    printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  // "solid", or binary data header
  if(!fgets(buffer, sizeof(buffer), fp)){ fclose(fp); return 0; }
  bool binary = strncmp(buffer, "solid", 5) && strncmp(buffer, "SOLID", 5);

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

  if(!binary){
    solid = 1;
    while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
      // 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 != NULL) {
          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 != NULL) {
          solid = 0;
        } else {
          // Read the XYZ coordinates if it is a vertex.
          bufferp = str;
          bufferp = strstr(bufferp, "vertex");
          if (bufferp != NULL) {
            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);
            }
          }
        }
      }
    }
  } // if(!binary)
  
  else {
    rewind(fp);
    while(!feof(fp)) {
      char header[80];
      if(!fread(header, sizeof(char), 80, fp)) break;
      unsigned int nfacets = 0;
      size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp);
      bool swap = false;
      if(nfacets > 100000000){
        //Msg::Info("Swapping bytes from binary file");
        swap = true;
        SwapBytes((char*)&nfacets, sizeof(unsigned int), 1);
      }
      if(ret && nfacets){
        //points.resize(points.size() + 1);
        char *data = new char[nfacets * 50 * sizeof(char)];
        ret = fread(data, sizeof(char), nfacets * 50, fp);
        if(ret == nfacets * 50){
          for(unsigned int i = 0; i < nfacets; i++) {
            float *xyz = (float *)&data[i * 50 * sizeof(char)];
            if(swap) SwapBytes((char*)xyz, sizeof(float), 12);
            for(int j = 0; j < 3; j++){
              //SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]);
              //points.back().push_back(p);
              //bbox += p;
              plist->newindex((void **) &coord);
              coord[0] = xyz[3 + 3 * j];
              coord[1] = xyz[3 + 3 * j + 1];
              coord[2] = xyz[3 + 3 * j + 2];
            }
          }
        }
        delete [] data;
      }
    } // while (!feof(fp))
  } // binary

  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)) != NULL) {
    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 == NULL) {
            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 {
              coord[j] = 0.0;
            }
            bufferp = findnextnumber(bufferp);
          }
        }
        continue;
      }
    }
    if ((ntets == 0) && istetmesh) { // Only read tetrahedra if 'istetmesh = 1'.
      // 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 == NULL) {
          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 == NULL) {
              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 == NULL) {
                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)
    }
  }

  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 Laboratory,   //
// 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)) != NULL) {
    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 == NULL) {
            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 = false;

  if (object == (int) tetgenbehavior::MEDIT) {
    success = load_medit(filebasename, 1);
  } else if (object == (int) tetgenbehavior::NEU_MESH) {
    //success = load_neumesh(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);
    load_elem(filebasename);
  }

  return success;
}

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

void tetgenio::save_nodes(const 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 != NULL ? 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 != NULL) {
      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 *) NULL)) {
    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(const 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 != NULL) {
        fprintf(fout, "  %d", trifacemarkerlist[i]);
      }
      fprintf(fout, "\n");
    }
  }

  fclose(fout);
}

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

void tetgenio::save_faces(const 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 != NULL ? 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 != NULL) {
      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 != NULL ? 1 : 0);
  for (i = 0; i < numberofedges; i++) {
    fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
            edgelist[i * 2 + 1]);
    if (edgemarkerlist != NULL) {
      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(const 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 != NULL ? 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 != NULL ? 1 : 0);
    for (i = 0; i < numberofedges; i++) {
      fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
              edgelist[i * 2 + 1]);
      if (edgemarkerlist != NULL) {
        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 != NULL ? 1 : 0);
    for (i = 0; i < numberoffacets; i++) {
      f = &(facetlist[i]);
      fprintf(fout, "%d  %d  %d  # %d\n", f->numberofpolygons,f->numberofholes,
            facetmarkerlist != NULL ? 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 != NULL ? 1 : 0);

  // Number of facets, number of boundary markers (zero or one).
  fprintf(fout, "%d  %d\n", numberoftrifaces, 
          trifacemarkerlist != NULL ? 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 != NULL) {
      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 NULL 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 *) NULL) {
      return (char *) NULL;
    }
    // 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 *) NULL) {
      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 [-pYrq_Aa_miO_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("    -r  Reconstructs a previously generated mesh.\n");
  printf("    -q  Refines mesh (to improve mesh quality).\n");
  printf("    -R  Mesh coarsening (to reduce the mesh elements).\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("    -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("    -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.6\n");
  printf("August, 2020\n");
  printf("\n");
  printf("Copyright (C) 2002 - 2020\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 the 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(NULL, 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_separate_ang_tol = (REAL) strtod(workstring, (char **) NULL);
        }
		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] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            //facet_overlap_ang_tol = (REAL) strtod(workstring, (char **) NULL);
          }
        }
        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';
            facet_small_ang_tol = (REAL) strtod(workstring, (char **) NULL);
          }
        }
        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';
            collinear_ang_tol = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } else if (argv[i][j] == 'Y') {
        nobisect++;
        if (cdt > 0) {
          printf("Warning:  switch -D is omitted.\n");
          cdt = 0;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
            supsteiner_level = (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;
        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';
          elem_growth_ratio = (REAL) strtod(workstring, (char **) NULL);
        }
        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';
            refine_progress_ratio = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } 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 **) NULL);
        }
        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 **) NULL);
          }
        }
      } else if (argv[i][j] == 'R') {
        coarsen = 1;
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
          coarsen_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')) ||
              (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';
            coarsen_percent = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } 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 **) NULL);
          }
        }
        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 = (REAL) strtod(workstring, (char **) NULL);
          }
        }
        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') {
        flipinsert = 1;
      } else if (argv[i][j] == 'm') {
        metric = 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] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          metric_scale = (REAL) strtod(workstring, (char **) NULL);
        }
      } 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 **) NULL);
          maxvolume_length = pow(maxvolume, 1./3.) / 3.;
        } else {
          varvolume = 1;
        }
      } else if (argv[i][j] == 'A') {
        regionattrib = 1;
      } else if (argv[i][j] == 'D') {
        if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '7')) {
          // -D# (with a number following it.)
          cdtrefine = (argv[i][j + 1] - '1') + 1;
          j++;
        } else {
          cdt = 1; // -D without a number following it.
        }
      } 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') {
        if (argv[i][j + 1] == '1') {  // -z1
          reversetetori = 1;
          j++;
        } else {
          zeroindex = 1; // -z
        }
      } 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] == 'g') {
        meditview = 1;
      } else if (argv[i][j] == 'k') {
        if (argv[i][j + 1] == '2') { // -k2
          vtksurfview = 1;
          j++;
        }
        else {
          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 **) NULL, 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] == ',')) { // -o/#
          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 **) NULL);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -o//#
          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] == '.') || (argv[i][j + 1] == 'e') ||
                   (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            opt_max_asp_ratio = (REAL) strtod(workstring, (char **) NULL);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -o///#
          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] == '.') || (argv[i][j + 1] == 'e') ||
                   (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            opt_max_edge_ratio = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } else if (argv[i][j] == 'O') {
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { // -O#
          opt_max_flip_level = (argv[i][j + 1] - '0');
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -O/#
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) {
            opt_scheme = (argv[i][j + 1] - '0');
            j++;
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -O//#
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
            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';
            opt_iterations = (int) strtol(workstring, (char **) NULL, 0);
            j++;
          }
        }
      } else if (argv[i][j] == 's') {
        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { // -s#
          smooth_cirterion = (argv[i][j + 1] - '0');
          j++;
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -s#/#
          j++;
          if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
            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';
            smooth_maxiter = (int) strtol(workstring, (char **) NULL, 0);
          }
        }
        if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -s#/#/#
          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] == '.') || (argv[i][j + 1] == 'e') ||
                   (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
              j++;
              workstring[k] = argv[i][j];
              k++;
            }
            workstring[k] = '\0';
            smooth_alpha = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } 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 **) NULL);
        }
      } else if (argv[i][j] == 'C') {
        docheck++;
      } else if (argv[i][j] == 'Q') {
        quiet = 1;
      } else if (argv[i][j] == 'W') {
        nowarning = 1;
      } else if (argv[i][j] == 'V') {
        verbose++;
      } else if (argv[i][j] == 'l') {
        //refine_list = 1; //incrflip = 1;
      } 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 **) NULL, 0);
          if (tetrahedraperblock > 8188) {
            vertexperblock = tetrahedraperblock / 2;
            shellfaceperblock = vertexperblock / 2;
          } else {
            tetrahedraperblock = 8188;
          }
        }
      } else if (argv[i][j] == 'H') {
        if (argv[i+1][0] != '-') {
          hole_mesh = 1;
          // It is a filename following by -H
          strncpy(hole_mesh_filename, argv[i+1], 1024 - 1);
          hole_mesh_filename[1024 - 1] = '\0';
          i++; // Skip the next string.
          break; // j
        }
      } 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(NULL, 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;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".neu")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = NEU_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 (refine && !quality) { // -r only
    // Reconstruct a mesh, no mesh optimization.
    opt_max_flip_level = 0;
    opt_iterations = 0;
  }
  if (insertaddpoints && (opt_max_flip_level == 0)) { // with -i option
    opt_max_flip_level = 2;
  }
  if (coarsen && (opt_max_flip_level == 0)) { // with -R option
    opt_max_flip_level = 2;
  }

  // Detect improper combinations of switches.
  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.
      }
    }
  }
  if (!quality) {
    // If no user-specified dihedral angle bound. Use default ones.
    if (optmaxdihedral == 177.0) { // set by -o/#
      optmaxdihedral = 179.9;
    }
  }

  if (quiet > 0) {
    verbose = 0; // No printf output during the execution.
  }

  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 soffset, toffset;
  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]]];
  }


  // 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 **) NULL;
  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 **) NULL) {
    // Walk through the top array.
    for (i = 0; i < toparraylen; i++) {
      // Check every pointer; NULLs may be scattered randomly.
      if (toparray[i] != (char *) NULL) {
        // Free an allocated block.
        free((void *) toparray[i]);
      }
    }
    // Free the top array.
    free((void *) toparray);
  }

  // The top array is no longer allocated.
  toparray = (char **) NULL;
  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 **) NULL) {
    // Allocate the top array big enough to hold 'topindex', and NULL 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 *) NULL;
    }
    // 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, NULL 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 *) NULL;
    }
    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 *) NULL) {
    // 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 NULL   //
//             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 **) NULL) {
    return (void *) NULL;
  }

  // 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 *) NULL;
  }

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

  // 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.             //
//                                                                            //
// 'newptr' returns a pointer to the new object (it must not be a NULL).      //
//                                                                            //
//============================================================================//

int tetgenmesh::arraypool::newindex(void **newptr)
{
  // Allocate an object at index 'firstvirgin'.
  int newindex = objects;
  *newptr = (void *) (getblock(objects) +
    (objects & (objectsperblock - 1)) * objectbytes);
  objects++;

  return newindex;
}


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

tetgenmesh::memorypool::memorypool()
{
  firstblock = nowblock = (void **) NULL;
  nextitem = (void *) NULL;
  deaditemstack = (void *) NULL;
  pathblock = (void **) NULL;
  pathitem = (void *) NULL;
  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 **) NULL) {
    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 **) NULL) {
    terminatetetgen(NULL, 1);
  }
  // Set the next block pointer to NULL.
  *(firstblock) = (void *) NULL;
  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 *) NULL;
}

//============================================================================//
//                                                                            //
// 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 *) NULL) {
    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 *) NULL) {
        // Allocate a new block of items, pointed to by the previous block.
        newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) 
                                    + alignbytes);
        if (newblock == (void **) NULL) {
          terminatetetgen(NULL, 1);
        }
        *nowblock = (void *) newblock;
        // The next block pointer is NULL.
        *newblock = (void *) NULL;
      }
      // 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 *) NULL;
  }
  // 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) NULL) {
    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 *) NULL) {
    // 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] != NULL) {
      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 *) NULL) {
    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] != NULL) {
      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 NULL. This makes it possible to detect
  //   dead tetrahedra when traversing the list of all tetrahedra.
  dyingtetrahedron[4] = (tetrahedron) NULL;

  // Dealloc the space to subfaces/subsegments.
  if (dyingtetrahedron[8] != NULL) {
    tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
  }
  if (dyingtetrahedron[9] != NULL) {
    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 *) NULL) {
      return (tetrahedron *) NULL;
    }
  } while ((newtetrahedron[4] == (tetrahedron) NULL) ||
           ((point) newtetrahedron[7] == dummypoint));
  return newtetrahedron;
}

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

  do {
    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
    if (newtetrahedron == (tetrahedron *) NULL) {
      return (tetrahedron *) NULL;
    }
  } while (newtetrahedron[4] == (tetrahedron) NULL); // 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 NULL. This makes it possible to detect dead
  //   shellfaces when traversing the list of all shellfaces.
  dyingsh[3] = (shellface) NULL;
  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 *) NULL) {
      return (shellface *) NULL;
    }
  } while (newshellface[3] == (shellface) NULL);          // 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) NULL) {
      return (point) NULL;
    }
  } 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] = NULL;
  newtet->tet[1] = NULL;
  newtet->tet[2] = NULL;
  newtet->tet[3] = NULL;
  // Four NULL vertices.
  newtet->tet[4] = NULL;
  newtet->tet[5] = NULL;
  newtet->tet[6] = NULL;
  newtet->tet[7] = NULL;
  // No attached segments and subfaces yet.
  newtet->tet[8] = NULL; 
  newtet->tet[9] = NULL;

  newtet->tet[10] = NULL; // used by mesh improvement

  // Init the volume to be zero.
  //REAL *polar = get_polar(newtet->tet);
  //polar[4] = 0.0;
  // 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;
}

void tetgenmesh::maketetrahedron2(triface* newtet, point pa, point pb,
                                  point pc, point pd)
{
  newtet->tet = (tetrahedron *) tetrahedrons->alloc();

  // Initialize the four adjoining tetrahedra to be "outer space".
  newtet->tet[0] = NULL;
  newtet->tet[1] = NULL;
  newtet->tet[2] = NULL;
  newtet->tet[3] = NULL;
  // Set four vertices.
  newtet->tet[4] = (tetrahedron) pa;
  newtet->tet[5] = (tetrahedron) pb;
  newtet->tet[6] = (tetrahedron) pc;
  newtet->tet[7] = (tetrahedron) pd; // may be dummypoint
  // No attached segments and subfaces yet.
  newtet->tet[8] = NULL; 
  newtet->tet[9] = NULL;

  newtet->tet[10] = NULL; // used by mesh improvement


  // 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 subsegments.                          //
//                                                                            //
//============================================================================//

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

  // No adjointing subfaces.
  newface->sh[0] = NULL;
  newface->sh[1] = NULL;
  newface->sh[2] = NULL;
  // Three NULL vertices.
  newface->sh[3] = NULL;
  newface->sh[4] = NULL;
  newface->sh[5] = NULL;
  // No adjoining subsegments.
  newface->sh[6] = NULL;
  newface->sh[7] = NULL;
  newface->sh[8] = NULL;
  // No adjoining tetrahedra.
  newface->sh[9] = NULL;
  newface->sh[10] = NULL;
  if (checkconstraints) {
    // Initialize the maximum area bound.
    setareabound(*newface, 0.0);
  }
  // Set the boundary marker to zero.
  setshellmark(*newface, 0);
  // Clear the infection and marktest bits.
  ((int *) (newface->sh))[shmarkindex + 1] = 0;
  if (useinsertradius) {
    setfacetindex(*newface, 0);
  }

  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, NULL);
  setpoint2ppt(*pnewpoint, NULL);
  if (b->plc || b->refine) {
    // Initialize the point-to-simplex field.
    setpoint2sh(*pnewpoint, NULL);
    if (b->metric && (bgm != NULL)) {
      setpoint2bgmtet(*pnewpoint, NULL);
    }
  }
  // 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 != NULL) {
    if (bgm->in->numberofpointattributes > numpointattrib) {
      numpointattrib = bgm->in->numberofpointattributes;
    }
  }
  if (addin != NULL) {
    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;
  }
  if (b->plc || b->refine || b->quality) {
    // Save the insertion radius for Steiner points if boundaries
    //   are allowed be split.
    //if (!b->nobisect || checkconstraints) {
      useinsertradius = 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;
    // The index within each point at which its u, v coordinates are found.
    // Comment: They are saved after the list of point attributes.
    pointparamindex = pointmtrindex - 2;
  } else {
    pointmtrindex = 3 + numpointattrib;
  }
  // 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 *) NULL) {
      // A background mesh is allocated. It may not exist though.
      sizeoftensor = (bgm->in != (tetgenio *) NULL) ? 
        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;
  }
  if (useinsertradius) {
    // Increase a space (REAL) for saving point insertion radius, it is
    //   saved directly after the metric. 
    sizeoftensor++;
  }
  pointinsradiusindex = pointmtrindex + sizeoftensor - 1;
  // 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);
  if (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 *) NULL)) {
      // 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 (indicated by pointmarkindex) plus:
  //   - an integer for boundary marker;
  //   - an integer for vertex type;
  //   - an integer for local index (for vertex insertion)
  pointsize = (pointmarkindex + 3) * 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, NULL);
  setpoint2ppt(dummypoint, NULL);
  if (b->plc || b->psc || b->refine) {
    // Initialize the point-to-simplex field.
    setpoint2sh(dummypoint, NULL);
    if (b->metric && (bgm != NULL)) {
      setpoint2bgmtet(dummypoint, NULL);
    }
  }
  // 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. 
  if (!(sizeof(int) <= sizeof(tetrahedron)) ||
      ((sizeof(tetrahedron) % sizeof(int)))) {
    terminatetetgen(this, 2);
  }
  elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int);

  // Let (cx, cy, cz) be the circumcenter of this element, r be the radius
  //   of the circumsphere, and V be the (positive) volume of this element.
  //   We save the following five values:
  //     2*cx, 2*cy, 2*cz, cx^2 + cy^2 + cz^2 - r^2 (height), 6*v.
  //   where the first four values define the polar plane of this element,
  //   the fifth value should be postive. It is used to guard the correctness
  //   of the polar plane.  Otherwise, use exact arithmetics to calculate.

  // The index within each element which its polar parameters are found,
  //   this index is measured in REALs.
  polarindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
  // 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);
  elemattribindex = polarindex; // polarindex + 5;
  // 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 the maximum volume 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) {
    if (b->refine && (in->refine_elem_list != NULL)) {
      b->varvolume = 1; // refine a given element list.
    }
  }
  if (b->varvolume) {
    elesize = (volumeboundindex + 1) * sizeof(REAL);
  } else {
    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 flags, and optionally one
	//   for storing facet index (for mesh refinement).
    shsize = (shmarkindex + 2 + useinsertradius) * 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);
  later_unflip_queue = new arraypool(sizeof(badface), 10);
  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);
  cave_oldtet_list = new arraypool(sizeof(tetrahedron*), 10);
}

//                                                                            //
//                                                                            //
//== mempool_cxx =============================================================//

//== geom_cxx ================================================================//
//                                                                            //
//                                                                            //

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

//============================================================================//
//                                                                            //
// 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 circumscribed 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)
{
  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]);
  if (oriB == 0.0) {
    terminatetetgen(this, 2);
  }
  // Flip the sign if there are odd number of swaps.
  if ((swaps % 2) != 0) oriB = -oriB;
  return oriB;
}

//============================================================================//
//                                                                            //
// 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 hyperplane 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)
{
  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]);
  if (oriB == 0.0) {
    terminatetetgen(this, 2);
  }
  // Flip the sign if there are odd number of swaps.
  if ((swaps % 2) != 0) oriB = -oriB;
  return oriB;
}

//============================================================================//
//                                                                            //
// 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 NULL, 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 return 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 == NULL) {
    // Calculate a lift point.
    if (1) {
      REAL n[3], len;
      // Calculate a lift point, saved in dummypoint.
      facenormal(A, B, C, n, 1, NULL);
      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.
        // !!! 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.
  }


  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) {
        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) {
          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) {
        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) {
          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) {
        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) {
          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 { // 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
        }
      }
    }
  }

  // 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, NULL, 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 {
          return (int) INTERSECT;
        }
      } else {
        if (types[0] == (int) SHAREEDGE) {
          return (int) SHAREEDGE;
        } else {
          return (int) INTERSECT;
        }
      }
    }
  }

  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;
  }

  // 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) {
    // op is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }
  if (abcpq == (int) SHAREEDGE) {
    // pq is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }
  if (abcqo == (int) SHAREEDGE) {
    // qo is coincident with an edge of abc.
    return (int) SHAREEDGE;
  }

  // They may share a vertex or disjoint.
  if (abcop == (int) SHAREVERT) {
    return (int) SHAREVERT;
  }
  if (abcpq == (int) SHAREVERT) {
    // q is the coincident vertex.
    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, NULL);
  area2[0] = dot(n1, n1);
  facenormal(pb, pa, pd, n2, 1, NULL);
  area2[1] = dot(n2, n2);

  if (area2[0] > area2[1]) {
    // Choose [a, b, c] as the base triangle.
    circumsphere(pa, pb, pc, NULL, c, &r);
    d = distance(c, pd);
  } else {
    // Choose [b, a, d] as the base triangle.
    if (area2[1] > 0) {
      circumsphere(pb, pa, pd, NULL, 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 NULL 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];
}

//============================================================================//
//                                                                            //
// 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, NULL);
  facenormal(pa, pb, pc2, n2, 1, NULL);
  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;
}

//============================================================================//
//                                                                            //
// 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 NULL 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;

  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 != NULL) {
    // 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;
}

REAL tetgenmesh::cos_interiorangle(REAL* o, REAL* p1, REAL* p2)
{
  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;

  costheta = dot(v1, v2) / lenlen;

  if (costheta > 1.0) {
    costheta = 1.0; // Roundoff.
  } else if (costheta < -1.0) {
    costheta = -1.0; // Roundoff.
  }

  return costheta;
}

//============================================================================//
//                                                                            //
// 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));
  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, NULL);
  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];
}

//============================================================================//
//                                                                            //
// 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 = NULL', 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 NULLs.  Otherwise, return FALSE, the four points are co-planar.    //
//                                                                            //
//============================================================================//

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 != NULL) {
    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 != NULL) {
    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 *) NULL) *radius = 0.0;
    return false;
  }    
  lu_solve(A, 3, indx, rhs, 0);
  if (cent != (REAL *) NULL) {
    cent[0] = pa[0] + rhs[0];
    cent[1] = pa[1] + rhs[1];
    cent[2] = pa[2] + rhs[2];
  }
  if (radius != (REAL *) NULL) {
    *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 *) NULL) *radius = 0.0;
    return false;
  }
  lu_solve(A, 4, indx, rhs, 0);

  if (orthocent != (REAL *) NULL) {
    orthocent[0] = rhs[1];
    orthocent[1] = rhs[2];
    orthocent[2] = rhs[3];
  }
  if (radius != (REAL *) NULL) {
    // 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 *U = e1, *V = e2;
  REAL Vuv[3]; // vector U->V

  Vuv[0] = V[0] - U[0];
  Vuv[1] = V[1] - U[1];
  Vuv[2] = V[2] - U[2];
  
  REAL A[4], B[4], C[4], D[4], O[4];

  A[0] = pa[0]; A[1] = pb[0]; A[2] = pc[0]; A[3] = -Vuv[0];
  B[0] = pa[1]; B[1] = pb[1]; B[2] = pc[1]; B[3] = -Vuv[1];
  C[0] = pa[2]; C[1] = pb[2]; C[2] = pc[2]; C[3] = -Vuv[2];
  D[0] = 1.;    D[1] = 1.;    D[2] = 1.;    D[3] = 0.;
  O[0] = 0.;    O[1] = 0.;    O[2] = 0.;    O[3] = 0.;
  
  REAL det, det1;

  det = orient4dexact(A, B, C, D, O, A[3], B[3], C[3], D[3], O[3]);

  if (det != 0.0) {
    det1 = orient3dexact(pa, pb, pc, U);

    *u = det1 / det;

    ip[0] = U[0] + *u * Vuv[0]; // (V[0] - U[0]);
    ip[1] = U[1] + *u * Vuv[1]; // (V[1] - U[1]);
    ip[2] = U[2] + *u * Vuv[2]; // (V[2] - U[2]);
  } else {
    *u = 0.0;
    ip[0] = ip[1] = ip[2] = 0.;
  }

}

//============================================================================//
//                                                                            //
// linelineint()    Calculate the intersection(s) of two line segments.       //
//                                                                            //
// Calculate the line segment [P, Q] that is the shortest route between 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(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7);
  vol[1] = orient4d(p3, p6, p2, p0, p1,  0, w6,  0, 0,  0);
  vol[2] = orient4d(p4, p6, p3, p0, p1, w4, w6,  0, 0,  0);
  vol[3] = orient4d(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 = NULL; // 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, NULL);
  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 != NULL) {
    // 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, NULL);
  len1 = sqrt(dot(n1, n1));
  facenormal(pa, pb, pd, n2, 1, NULL);
  len2 = sqrt(dot(n2, n2));
  if (len1 > len2) {
    norm = n1;
    len = len1;
  } else {
    norm = n2;
    len = len2;
  }
  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 ================================================================//
//                                                                            //
//                                                                            //

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

//============================================================================//
//                                                                            //
// 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., to 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., to rotate the three tets counterclockwisely (right-hand rule)  //
//       until a (or b) is in c's position.                                   //
//                                                                            //
// If 'fc->enqflag > 0', faces on the convex hull of {a,b,c,d,e} will be      //
// queued for flipping.                                                       //
// In particular, if 'fc->enqflag = 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] != NULL) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = NULL;
    }
    if (fliptets[1].tet[8] != NULL) {
      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
      fliptets[1].tet[8] = NULL;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != NULL) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = NULL;
    }
    if (fliptets[1].tet[9] != NULL) {
      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
      fliptets[1].tet[9] = NULL;
    }
  }
  // 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 != NULL) {
        // 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] != NULL) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = NULL;
    }
    if (fliptets[1].tet[8] != NULL) {
      tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
      fliptets[1].tet[8] = NULL;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != NULL) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = NULL;
    }
    if (fliptets[1].tet[9] != NULL) {
      tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
      fliptets[1].tet[9] = NULL;
    }
  }
  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 == NULL) {
        tsbond(topcastets[0], flipfaces[0]);
        fsymself(topcastets[0]);
        sesymself(flipfaces[0]);
        tsbond(topcastets[0], flipfaces[0]);
      } else {
        // An invalid 2-to-2 flip. Report a bug.
        terminatetetgen(this, 2); 
      }
      // 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 == NULL) {
        tsbond(botcastets[0], flipfaces[1]);
        fsymself(botcastets[0]);
        sesymself(flipfaces[1]);
        tsbond(botcastets[0], flipfaces[1]);
      } else {
        // An invalid 2-to-2 flip. Report a bug.
        terminatetetgen(this, 2);  
      }
    } // 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 != NULL) {
        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]}.
        // 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]);
          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] != NULL) {
      tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
      fliptets[0].tet[8] = NULL;
    }
  }
  if (checksubfaceflag) {
    // Dealloc the space to subfaces.
    if (fliptets[0].tet[9] != NULL) {
      tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
      fliptets[0].tet[9] = NULL;
    }
  }
  // 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, hulledgeflag;
  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.
      hulledgeflag = 0;
      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 immediately by a 3-to-2 flip.
                reducflag = 1;
                // Check if [e,d] is a hull edge.
                pf = apex(abtets[(i + 2) % n]);
                hulledgeflag = (pf == dummypoint);
              }
            }
          }
        }
        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]);
          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-planar case).
              hulledgeflag = 1; // [e,d] is a hull edge.
            }
          }
        }
      } // if (hullflag)

      if (reducflag) {
        if (nonconvex && hulledgeflag) {
          // We will create a hull edge [e,d]. Make sure it does not exist.
          if (getedge(pe, pd, &spintet)) {
            // The 2-to-3 flip is not a topological valid flip. 
            reducflag = 0;
          }
        }
      }



      if (reducflag) {
        triface checktet = abtets[i];
        if (!valid_constrained_f23(checktet, pd, pe)) {
          reducflag = 0;
        }
      }

      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 remembers the vertex 'c' (in 'abtets[n-1].tet'), and
          //  (ii) it remembers the position [i] where this flip took place.
          // These information let us to either undo this flip or recover
          //   the original edge link (for collecting new created tets).
          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 degenerated
              //   tet. It must be removed. 
              // Remember 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]
              // 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 (unflip || (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.
          int subface_count = 0;
          n1 = 0;
          j = 0; // Sum of the star counters.
          spintet = flipedge;
          while (1) {
            if (issubface(spintet)) subface_count++;
            n1++;
            j += (elemcounter(spintet)); 
            fnextself(spintet);
            if (spintet.tet == flipedge.tet) break;
          }
          if (n1 < 3) {
            // This is only possible when the mesh contains inverted
            //   elements.  Reprot a bug.
            terminatetetgen(this, 2);
          }
          if (j > 2) {
            // The Star(flipedge) overlaps other Stars.
            continue; // Do not flip this edge.
          }

          if (fc->noflip_in_surface) {
            if (subface_count > 0) {
              continue;
            }
          }

          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).
          spintet = flipedge;
          for (j = 0; j < n1; j++) {
            tmpabtets[j] = spintet;
            // Increase the star counter of this tet.
            increaseelemcounter(tmpabtets[j]);
            fnextself(spintet);
          }

          // 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
            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]).
                // 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++) {
                  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 selected edge is not flipped.
            if (!fc->unflip) {
              // 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++) {
              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 = NULL, chkpt;
        REAL bigvol = 0.0, ori1, ori2;
        // Search an interior vertex which is an apex of edge [c,d].
        //   In principle, it can be arbitrary 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);
            if (ori > bigvol) {
              bigvol = ori;
              searchpt = chkpt;
            }
          }
        }
        if (searchpt != NULL) { 
          // 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;
          }
        }
        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.
            }
          }
        } else if (nn == 3) {
          // Report a bug.
          terminatetetgen(this, 2);
        }
      }
      
      if (!rejflag) {
        if (!valid_constrained_f32(abtets, pa, pb)) {
          rejflag = 1;
        }
      }

      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 {
              *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);
      if (fc->unflip) {
        if (b->verbose > 3) {
          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);
      if (fc->unflip) {        
        if (b->verbose > 3) {
          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 > 3) {
        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 != NULL) {
    loc = (enum locateresult) ivf->iloc;
  }

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

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

  if (b->weighted) {
    if (loc != OUTSIDE) {
      // Check if this vertex is regular.
      pts = (point *) searchtet->tet;
      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 lies above the lower hull. Do not insert it.
        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 != NULL) && (splitsh->sh != NULL)) {
        // 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 != NULL) && (splitseg->sh != NULL)) {
        smarktest(*splitseg);
        splitseg->shver = 0;
        spivot(*splitseg, *splitsh);
      }
      if (splitsh != NULL) {
        if (splitsh->sh != NULL) {
          // 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 == NULL) 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.
    if (cavebdrylist->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;
          }
        }
      }
    } // if (cavebdrylist->objects == 0)
  } else if (loc == ONVERTEX) {
    // The point already exist. Do nothing and return.
    return 0;
  } else if (loc == ENCSUBFACE) {
    ivf->iloc = (int) ENCSUBFACE;
    return 0;
  } else {
    // Unknown case
    terminatetetgen(this, 2);
  }

  if (ivf->collect_inial_cavity_flag) {
    tetrahedron **ptptr;
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      cave_oldtet_list->newindex((void **) &ptptr);
      *ptptr = cavetet->tet;
    }
    // Do not insert this point.
    insertpoint_abort(splitseg, ivf);
    //  ivf->iloc = NULLCAVITY;
    return 0;
  } // if (ivf->collect_inial_cavity_flag)

  if ((b->plc || b->quality) && (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, ins_radius;
    pts = NULL;

    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 < minedgelength) {
        if ((!create_a_shorter_edge(insertpt, *parypt)) &&
            (!ivf->ignore_near_vertex)) {
          pts = parypt;
          loc = NEARVERTEX;
          break;
        }
      }
      if (ivf->check_insert_radius) { //if (useinsertradius) {
        ins_radius = getpointinsradius(*parypt);
        if (ins_radius > 0.0) {
          if (rd < ins_radius) {
            if (!create_a_shorter_edge(insertpt, *parypt)) {
              // Reject the isnertion of this vertex.
              pts = parypt;
              loc = ENCVERTEX;
              break;
            }
          }
        }
      }
      if (rejptflag) {
        // Is the point encroaches upon an existing point?
        if (rd < (0.5 * (*parypt)[pointmtrindex])) {
          pts = parypt;
          loc = ENCVERTEX; 
          break;
        }
      }
    }
    cavetetvertlist->restart(); // Clear the work list.

    if (pts != NULL) {
      // The point is either too close to an existing vertex (NEARVERTEX)
      //   or encroaches upon (inside the protecting ball) of that vertex.
      if (loc == NEARVERTEX) {
        point2tetorg(*pts, *searchtet);
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) loc;
        return 0;
      } else { // loc == ENCVERTEX
        // The point lies inside the protection ball.
        point2tetorg(*pts, *searchtet); 
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) loc;
        return 0;
      }
    }
  } // if ((b->plc || b->quality) && (loc != INSTAR))


  if (ivf->assignmeshsize) {
    // Assign mesh size for the new point.
    if (bgm != NULL) {
      // Interpolate the mesh size from the background mesh. 
      bgm->decode(point2bgmtet(org(*searchtet)), neightet);
      int bgmloc = (int) bgm->scout_point(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. 
                // Include it in the cavity. The convex hull will be enlarged.
                enqflag = true; 
              } 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;
                    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;
                  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 (ivf->refineflag > 0) {
    // 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))) {
      insertpoint_abort(splitseg, ivf);
      ivf->iloc = (int) BADELEMENT;
      return 0;
    }
  } // if (ivf->refineflag)

  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]) != NULL) {
        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);
        point *ppt = (point *) &(paryseg1->sh[3]);
        if (check_encroachment(ppt[0], ppt[1], insertpt)) {
          badface *bf = NULL;
          encseglist->newindex((void **) &bf);
          bf->init();
          bf->ss = *paryseg1;
          bf->forg = sorg(bf->ss);
          bf->fdest = sdest(bf->ss);
        }
      } // i
      if ((ivf->rejflag & 1) && (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]) != NULL) {
        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 ccent[3], radius;
      badface *bface;
      // Reject this point if it encroaches upon any subface.
      for (i = 0; i < cavetetshlist->objects; i++) {
        parysh = (face *) fastlookup(cavetetshlist, i);
        if (get_subface_ccent(parysh, ccent)) {
          point encpt = insertpt;
          if (check_enc_subface(parysh, &encpt, ccent, &radius)) {
            encshlist->newindex((void **) &bface);
            bface->ss = *parysh;
            bface->forg = sorg(*parysh);
            bface->fdest = sdest(*parysh);
            bface->fapex = sapex(*parysh);
            bface->noppo = NULL; // no existing encroaching vertex.
            for (j = 0; j < 3; j++) bface->cent[j] = ccent[j];
            for (j = 3; j < 6; j++) bface->cent[j] = 0.;
            bface->key = radius;
          }
        }
      }
      if (encshlist->objects > 0) {
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) ENCSUBFACE;
        return 0;
      }
    }
  } // if (checksubfaceflag)

  if ((ivf->iloc == (int) OUTSIDE) && ivf->refineflag) {
    // The vertex lies outside of the domain. And it does not encroach
    //   upon any boundary segment or subface. Do not insert it.
    insertpoint_abort(splitseg, ivf);
    return 0;
  }

  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);
      checksh = *parysh;
      for (j = 0; j < 3; j++) {
        if (!isshsubseg(checksh)) {
          spivot(checksh, neighsh);
          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.
              terminatetetgen(this, 2); 
            }
            neightet = spintet;
            if (b->verbose > 4) {
               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) {
            // Check if this face is visible by the new point.
            if (issubface(neightet)) {
              // Re-use 'volume' and 'attrib'.
              pa = org(*cavetet);
              pb = dest(*cavetet);
              pc = apex(*cavetet);
              volume = orient3dfast(pa, pb, pc, insertpt);
              attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa);
              if ((fabs(volume) / attrib) < b->epsilon) {
                ori = 0.0;
              } else {
                ori = orient3d(pa, pb, pc, insertpt); 
              }
            } else {
              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) NULLCAVITY; // 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 != NULL) && (splitsh->sh != NULL)) {
              // The to-be split subface should be in sC(p).
              if (!smarktested(*splitsh)) i++;
            }
          } else if (loc == ONEDGE) {
            if ((splitseg != NULL) && (splitseg->sh != NULL)) {
              // The to-be split segment should be in sC(p).
              if (!smarktested(*splitseg)) i++;
            }
            if ((splitsh != NULL) && (splitsh->sh != NULL)) {
              // 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 == NULL) break;
              } // while (1)
            }
          }

          if (i > 0) {
            // The updated sC(p) is invalid. Do not insert this vertex.
            insertpoint_abort(splitseg, ivf);
            ivf->iloc = (int) NULLCAVITY; // 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;
    } else {
      // The following options are used in boundary recovery when we try to
      //   remove a crossing face (ivf->refineflag == 4) or a crossing edge
      //   (ivf->refineflag == 8). Reject this point if the face(or edge)
      //   survives after inserting this vertex.
      bool bflag = false;
      if (ivf->refineflag == 4) {
        // Check if the face (ivf.refinetet) is removed.
        // Both tets at this face should be in the cavity.
        triface adjtet;
        fsym(ivf->refinetet, adjtet);
        if (!infected(ivf->refinetet) || !infected(adjtet)) {
          bflag = true;
        }
      } else if (ivf->refineflag == 8) {
        // Check if the edge (ivf.refinetet) is removed.
        // All tets at this edge should be in the cavity.
        triface spintet = ivf->refinetet;
        while (true) {
          if (!infected(spintet)) {
            bflag = true; break;
          }
          fnextself(spintet);
          if (spintet.tet == ivf->refinetet.tet) break;
        }
      }
      if (bflag) {
        // Reject this new point.
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) BADELEMENT;
        return 0;
      }
    }
  } // if (ivf->refineflag)

  if ((splitseg != NULL) && (splitseg->sh != NULL)) {
    // A segment will be split. It muts lie inside of the cavity.
    sstpivot1(*splitseg, neightet);
    if (neightet.tet != NULL) {
      // This is an existing segment.
      bool bflag = false;
      spintet = neightet;
      while (true) {
        if (!infected(spintet)) {
          bflag = true; break;
        }
        fnextself(spintet);
        if (spintet.tet == neightet.tet) break;
      }
      if (bflag) {
        // Reject this new point.
        insertpoint_abort(splitseg, ivf);
        ivf->iloc = (int) BADELEMENT;
        return 0;
      }
    } // if (neightet.tet != NULL)
  }

  if (b->weighted || ivf->cdtflag || ivf->smlenflag || ivf->validflag) {
    // 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->smlenflag) {
      REAL len;
      // Get the length of the shortest edge connecting to 'newpt'.
      parypt = (point *) fastlookup(cavetetvertlist, 0);
      ivf->smlen = distance(*parypt, insertpt);
      ivf->parentpt = *parypt;
      for (i = 1; i < cavetetvertlist->objects; i++) {
        parypt = (point *) fastlookup(cavetetvertlist, i);
        len = distance(*parypt, insertpt);
        if (len < ivf->smlen) {
          ivf->smlen = len;
          ivf->parentpt = *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();
    }
    ivf->iloc = (int) INSTAR;
    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.
            terminatetetgen(this, 2);
          }
        }
      } 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; // Remember 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.
            terminatetetgen(this, 2);
          }
        }
      } 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] == NULL) {
        // Find the adjacent face of this newtet.
        spintet = oldtet;
        while (1) {
          fnextself(spintet);
          if (!infected(spintet)) break;
        }
        fsym(spintet, newneitet);
        esymself(newneitet);
        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 != NULL) && (splitsh->sh != NULL)) ||
      ((splitseg != NULL) && (splitseg->sh != NULL))) {
    // 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] != NULL) {
          // 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);
          // Find the adjacent tet containing the edge [a,b] outside C(p).
          spintet = neightet;
          while (1) {
            fnextself(spintet);
            if (!infected(spintet)) break;
            if (spintet.tet == neightet.tet) {
              terminatetetgen(this, 2);
            }
          }
          // The adjacent tet connects to a new tet in C(p).
          fsym(spintet, neightet);
          // Find the tet containing the face [a, b, p].
          spintet = neightet;
          while (1) {
            fnextself(spintet);
            if (apex(spintet) == insertpt) break;
          }
          // Adjust the edge direction in spintet and checksh.
          if (sorg(checksh) != org(spintet)) {
            sesymself(checksh);
          }
          // Connect the subface to two adjacent tets.
          tsbond(spintet, checksh);
          fsymself(spintet);
          sesymself(checksh);
          tsbond(spintet, checksh);
        } // if (checksh.sh[3] != NULL)
      }
    } else { 
      // The Boundary recovery 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] != NULL) {
          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);
        // 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 != NULL) {
        // 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 != NULL) {
            // Get the adjacent new tetrahedron.
            stpivot(checksh, neightet);
          } else {
            // It's a dangling segment.
            point2tetorg(sorg(checkseg), neightet);
            finddirection(&neightet, sdest(checkseg));
          }
          if (isdeadtet(neightet)) {
            terminatetetgen(this, 2);
          }
          sstbond1(checkseg, neightet);
          spintet = neightet;
          while (1) {
            tssbond1(spintet, checkseg);
            fnextself(spintet);
            if (spintet.tet == neightet.tet) break;
          }
        }
      } // if (splitseg != NULL)
    } else {
      // The Boundary Recovery Phase.  
      // Queue missing segments in C(p) for recovery.
      if (splitseg != NULL) {
        // 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.
          subsegstack->newindex((void **) &paryseg);
          *paryseg = checkseg;
        }
      } // if (splitseg != NULL)
      for (i = 0; i < caveencseglist->objects; i++) {
        paryseg = (face *) fastlookup(caveencseglist, i);
        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 || ivf->validflag) {
    // Some vertices may be completed inside the cavity. They must be
    //   detected and added to recovering list.
    for (i = 0; i < cavetetvertlist->objects; i++) {
      pts = (point *) fastlookup(cavetetvertlist, i);
      decode(point2tet(*pts), *searchtet);
      if (infected(*searchtet)) {
        if (b->weighted) {
          if (b->verbose > 4) {
            printf("    Point #%d is non-regular after the insertion of #%d.\n",
                   pointmark(*pts), pointmark(insertpt));
          }
          setpointtype(*pts, NREGULARVERTEX);
          nonregularcount++;
        } else {
          if (b->verbose > 4) {
            printf("        Deleting an interior vertex %d.\n", pointmark(*pts));
          }
          // The cavity is updated such that no constrained segments and
          //   subfaces are in its interior. Interior vertices must be
          //   inside volume or on a boundary facet.
          // The point has been removed.
          point steinerpt = *pts;
          enum verttype vt = pointtype(steinerpt);
          if (vt != UNUSEDVERTEX) {
            setpointtype(steinerpt, UNUSEDVERTEX);
            unuverts++;
          }
          if (vt != VOLVERTEX) {
            // 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++;
          }
        }
      }
    }
  }

  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 != NULL) {
      // 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] != NULL) {
        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 != NULL) && (splitsh->sh != NULL)) ||
      ((splitseg != NULL) && (splitseg->sh != NULL))) {
    // 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 != NULL) {
          if (neightet.tet[4] != NULL) {
            // Found an adjacent tet. It must be not in C(p).
            tsdissolve(neightet);
            fsymself(neightet);
            tsdissolve(neightet);
          }
        }
      }
      shellfacedealloc(subfaces, parysh->sh);
    }
    if ((splitseg != NULL) && (splitseg->sh != NULL)) {
      // 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);
      }
    }
  }


  // 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->smlenflag || ivf->validflag) {
    cavetetvertlist->restart();
  }
  
  if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
      ((splitseg != NULL) && (splitseg->sh != NULL))) {
    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 != NULL) && (splitseg->sh != NULL)) {
      sunmarktest(*splitseg);
    }
    for (i = 0; i < caveshlist->objects; i++) {
      parysh = (face *) fastlookup(caveshlist, i);
      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, mtr;
  int coordindex;
  int attribindex;
  int mtrindex;
  int i, j;

  // 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++) {
      mtr = in->pointmtrlist[mtrindex++] * b->metric_scale;
      pointloop[pointmtrindex + j] = mtr; // 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 largest 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;
    }
  }

  x = xmax - xmin;
  y = ymax - ymin;
  z = zmax - zmin;

  exactinit(b->verbose, b->noexact, b->nostaticfilter, x, y, z);

  // Use the number of points as the random seed.
  srand(in->numberofpoints);

  // 'longest' is the largest possible edge length formed by input vertices.
  longest = sqrt(x * x + y * y + z * z);
  if (longest == 0.0) {
    printf("Error:  The point set is trivial.\n");
    terminatetetgen(this, 10);
  }
  // Two identical points are distinguished by 'minedgelength'.
  minedgelength = longest * b->epsilon;

#ifndef TETLIBRARY
  /*
  // Release the memory from the input data strutcure
  delete [] in->pointlist;
  in->pointlist = NULL;
  if (in->pointattributelist != NULL) {
    delete [] in->pointattributelist;
    in->pointattributelist = NULL;
  }
  if (in->pointmtrlist != NULL) {
    delete [] in->pointmtrlist;
    in->pointmtrlist = NULL;
  }
  */
#endif
}

//============================================================================//
//                                                                            //
// 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);
      }
    } // 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-arrays such that left points
  //   have Hilbert indices lower than the right points.
  i = 0;
  j = arraysize - 1;

  // Partition the vertices into left- and right-arrays.
  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;
    }
  }

  // Recursively 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) {
      // Calculcate 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 = 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 searching 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 == NULL) {
      // A null tet. Choose the recenttet as the starting tet.
      *searchtet = recenttet;
    }

    // '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'.
    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;
  if (sampleblocks == 0) {
    sampleblocks = 1; // at least one sample block is needed.
  }
  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) NULL) {
        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_dt(point searchpt, triface* searchtet)
{
  //enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
  REAL ori, oriorg, oridest, oriapex;
  enum locateresult loc = OUTSIDE;
  point toppo;
  int s, i;

  if (searchtet->tet == NULL) {
    searchtet->tet = recenttet.tet;
  }

  if (ishulltet(*searchtet)) {
    // Get its adjacent tet (inside the hull).
    searchtet->tet = decode_tet_only(searchtet->tet[3]);
  }

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

  if (searchtet->ver == 4) {
    terminatetetgen(this, 2);
  }

  // Walk through tetrahedra to locate the point.
  do {

    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?
    // Randomly choose one of three faces (containig  toppo) of this tet.
    s = rand() % 3; // s \in \{0,1,2\}
    for (i = 0; i < s; i++) enextself(*searchtet);

    oriorg = orient3d(dest(*searchtet), apex(*searchtet), toppo, searchpt);
    if (oriorg < 0) {
      //nextmove = ORGMOVE;
      enextesymself(*searchtet);
    } else {
      oridest = orient3d(apex(*searchtet), org(*searchtet), toppo, searchpt);
      if (oridest < 0) {
        //nextmove = DESTMOVE;
        eprevesymself(*searchtet);
      } else {
        oriapex = orient3d(org(*searchtet), dest(*searchtet), toppo, searchpt);
        if (oriapex < 0) {
          //nextmove = APEXMOVE;
          esymself(*searchtet);
        } else {
          // oriorg >= 0, oridest >= 0, oriapex >= 0 ==> found the point.
          // The point we seek must be on the boundary of or inside this
          //   tetrahedron. Check for boundary cases first.
          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;
          break;
        }
      }
    } // if (locateflag)

    // Move to the next tet adjacent to the selected face.
    decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself

    if (ishulltet(*searchtet)) {
      loc = OUTSIDE; // return OUTSIDE;
      break;
    }

  } while (true);

  return loc;
}

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;

  torg = tdest = tapex = toppo = NULL;

  if (searchtet->tet == NULL) {
    // 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->tet = decode_tet_only(searchtet->tet[3]);
  }

  // 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;
  }
  if (searchtet->ver == 4) {
    terminatetetgen(this, 2);
  }

  // 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).
    decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself
    if (ishulltet(*searchtet)) {
      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;
}

//============================================================================//
//                                                                            //
// insert_vertex_bw()    Insert a vertex using the Bowyer-Watson algorithm.   //
//                                                                            //
// This function is only used for initial Delaunay triangulation construction.//
// It improves the speed of incremental algorithm.                            //
//                                                                            //
//============================================================================//

int  tetgenmesh::insert_vertex_bw(point insertpt, triface *searchtet,
  insertvertexflags *ivf)
{
  tetrahedron **ptptr, *tptr;
  triface cavetet, spintet, neightet, neineitet, *parytet;
  triface oldtet, newtet; //, newneitet;
  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 != NULL) {
    loc = (enum locateresult) ivf->iloc;
  }

  if (loc == OUTSIDE) {
    if (searchtet->tet == NULL) {
      if (!b->weighted) {
        randomsample(insertpt, searchtet);
      } else {
        // Weighted DT. There may exist dangling vertex. 
        *searchtet = recenttet;
      }
    }
    loc = locate_dt(insertpt, searchtet);
  }

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

  if (b->weighted) {
    if (loc != OUTSIDE) {
      // Check if this vertex is regular.
      pts = (point *) searchtet->tet;
      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 lies above the lower hull. Do not insert it.
        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 (loc == OUTSIDE) {
    infect(*searchtet);
    cave_oldtet_list->newindex((void **) &ptptr);
    *ptptr = searchtet->tet;
  } else if (loc == INTETRAHEDRON) {
    infect(*searchtet);
    cave_oldtet_list->newindex((void **) &ptptr);
    *ptptr = searchtet->tet;
  } else if (loc == ONFACE) {
    infect(*searchtet);
    cave_oldtet_list->newindex((void **) &ptptr);
    *ptptr = searchtet->tet;
    neightet.tet = decode_tet_only(searchtet->tet[searchtet->ver & 3]);
    infect(neightet);
    cave_oldtet_list->newindex((void **) &ptptr);
    *ptptr = neightet.tet;
  } else if (loc == ONEDGE) {

    // Add all adjacent boundary tets into list.
    spintet = *searchtet;
    while (1) {
      infect(spintet);
      cave_oldtet_list->newindex((void **) &ptptr);
      *ptptr = spintet.tet;
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    } // while (1)
  } else if (loc == ONVERTEX) {
    // The point already exist. Do nothing and return.
    return 0;
  } 

  // Create the cavity C(p).

  for (i = 0; i < cave_oldtet_list->objects; i++) {
    ptptr = (tetrahedron **) fastlookup(cave_oldtet_list, i);
    cavetet.tet = *ptptr;
    for (cavetet.ver = 0; cavetet.ver < 4; cavetet.ver++) {
      neightet.tet = decode_tet_only(cavetet.tet[cavetet.ver]);
      if (!infected(neightet)) {
        // neightet.tet is current outside the cavity.
        enqflag = false;
        if (!marktested(neightet)) {
          if (!ishulltet(neightet)) {
            pts = (point *) neightet.tet;
            sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt);
            enqflag = (sign < 0.0);
          } else {
            pts = (point *) neightet.tet;
            ori = orient3d(pts[4], pts[5], pts[6], insertpt);
            if (ori < 0) {
              // A visible hull face.
              enqflag = true;
            } else if (ori == 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.
              triface neineitet;
              neineitet.tet = decode_tet_only(neightet.tet[3]);
              pts = (point *) neineitet.tet;
              sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
              enqflag = (sign < 0.0);
            }
          }
          marktest(neightet);
        }
        if (enqflag) {
          infect(neightet);
          cave_oldtet_list->newindex((void **) &ptptr);
          *ptptr = neightet.tet;
        } else {
          // A boundary face.
          cavebdrylist->newindex((void **) &parytet);
          *parytet = cavetet;
        }
      } // if (!infected(neightet))
    }
  } // i

  // Create new tetrahedra to fill the cavity.
  int f_out = cavebdrylist->objects;
  int v_out = (f_out + 4) / 2;  


  triface *pcavetet;
  point V[3];
  int local_vcount = 0; // local index of vertex
  int sidx[3];

  static int row_v08_tbl[12] = {8,9,10,11,0,1,2,3,4,5,6,7};
  static int row_v11_tbl[12] = {8,9,10,11,0,1,2,3,4,5,6,7};
  static int col_v01_tbl[12] = {1,1,1,1,5,5,5,5,9,9,9,9};
  static int col_v02_tbl[12] = {2,2,2,2,6,6,6,6,10,10,10,10};
  static int col_v08_tbl[12] = {8,8,8,8,0,0,0,0,4,4,4,4};
  static int col_v11_tbl[12] = {11,11,11,11,3,3,3,3,7,7,7,7};

  triface *tmp_bw_faces = NULL;
  int shiftbits = 0;

  if (v_out < 64) {
    shiftbits = 6;
    tmp_bw_faces = _bw_faces;
  } else if (v_out < 1024) {
    // Dynamically allocate an array to store the adjacencies.
    int arysize = 1;
    int tmp = v_out;
    shiftbits = 1;
    while ((tmp >>= 1)) shiftbits++;
    arysize <<= shiftbits;
    tmp_bw_faces = new triface[arysize * arysize];
  } 

  if (v_out < 1024) {
    for (i = 0; i < f_out; i++) {
      pcavetet = (triface *) fastlookup(cavebdrylist, i);
      oldtet = *pcavetet;

      // Get the tet outside the cavity.
      decode(oldtet.tet[oldtet.ver], neightet);
      unmarktest(neightet);

      if (ishulltet(oldtet)) {
        // neightet.tet may be also a hull tet (=> oldtet is a hull edge).
        neightet.ver = epivot[neightet.ver];
        if ((apex(neightet) == dummypoint)) {
          hullsize++; // Create a new hull tet.
        }
      }
 
      // Create a new tet in the cavity.
      V[0] = dest(neightet);
      V[1] = org(neightet);
      V[2] = apex(neightet);
      maketetrahedron2(&newtet, V[1], V[0], insertpt, V[2]);
      //bond(newtet, neightet);
      newtet.tet[2] = encode2(neightet.tet, neightet.ver);
      neightet.tet[neightet.ver & 3] = encode2(newtet.tet, col_v02_tbl[neightet.ver]);

      // Fill the adjacency matrix, and count v_out.
      for (j = 0; j < 3; j++) {
        tptr = (tetrahedron *) point2tet(V[j]);
        if (((point *) tptr)[6] != insertpt) {
          // Found a unique vertex of the cavity.
          setpointgeomtag(V[j], local_vcount++);
          //local_vcount++;
          setpoint2tet(V[j], (tetrahedron) (newtet.tet));
        }
        sidx[j] = pointgeomtag(V[j]);
      } // j
      
      neightet.tet = newtet.tet;
      // Avoid using lookup tables.
      neightet.ver = 11;
      tmp_bw_faces[(sidx[1] << shiftbits) | sidx[0]] = neightet;
      neightet.ver = 1;
      tmp_bw_faces[(sidx[2] << shiftbits) | sidx[1]] = neightet;
      neightet.ver = 8;
      tmp_bw_faces[(sidx[0] << shiftbits) | sidx[2]] = neightet;
      
      *pcavetet = newtet;
    } // i // f_out

    // Set a handle for speeding point location.
    // Randomly pick a new tet.
    i = rand() % f_out;
    recenttet = * (triface *) fastlookup(cavebdrylist, i);    
    setpoint2tet(insertpt, (tetrahedron) (recenttet.tet));

    for (i = 0; i < f_out; i++) {
      neightet = * (triface *) fastlookup(cavebdrylist, i);
      if (neightet.tet[3] == NULL) {
        neightet.ver = 11;
        j = pointgeomtag(org(neightet));
        k = pointgeomtag(dest(neightet));
        neineitet = tmp_bw_faces[(k << shiftbits) | j];
        // bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12);
        neightet.tet[3] = encode2(neineitet.tet, row_v11_tbl[neineitet.ver]);
        neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v11_tbl[neineitet.ver]);
      }
      if (neightet.tet[1] == NULL) {
        neightet.ver = 1;
        j = pointgeomtag(org(neightet));
        k = pointgeomtag(dest(neightet));
        neineitet = tmp_bw_faces[(k << shiftbits) | j];
        neightet.tet[1] = encode2(neineitet.tet, neineitet.ver); // row_v01_tbl
        neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v01_tbl[neineitet.ver]);
      }
      if (neightet.tet[0] == NULL) {
        neightet.ver = 8;
        j = pointgeomtag(org(neightet));
        k = pointgeomtag(dest(neightet));
        neineitet = tmp_bw_faces[(k << shiftbits) | j];
        // bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12);
        neightet.tet[0] = encode2(neineitet.tet, row_v08_tbl[neineitet.ver]);
        neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v08_tbl[neineitet.ver]);
      }    
    } // i
    
    if (v_out >= 64) {
      delete [] tmp_bw_faces;
    }
  } // v_out < 1024
  else {
    // Fill a very large cavity with original neighboring searching method.
    for (i = 0; i < f_out; i++) {
      pcavetet = (triface *) fastlookup(cavebdrylist, i);
      oldtet = *pcavetet;

      // Get the tet outside the cavity.
      decode(oldtet.tet[oldtet.ver], neightet);
      unmarktest(neightet);

      if (ishulltet(oldtet)) {
        // neightet.tet may be also a hull tet (=> oldtet is a hull edge).
        neightet.ver = epivot[neightet.ver];
        if ((apex(neightet) == dummypoint)) {
          hullsize++; // Create a new hull tet.
        }
      }
 
      // Create a new tet in the cavity.
      V[0] = dest(neightet);
      V[1] = org(neightet);
      V[2] = apex(neightet);
      maketetrahedron2(&newtet, V[1], V[0], insertpt, V[2]);
      //newtet.ver = 2; // esymself(newtet);
      //assert(oppo(newtet) == insertpt);
 
      //bond(newtet, neightet);
      newtet.tet[2] = encode2(neightet.tet, neightet.ver);
      neightet.tet[neightet.ver & 3] = encode2(newtet.tet, col_v02_tbl[neightet.ver]);

      // Fill the adjacency matrix, and count v_out.
      for (j = 0; j < 3; j++) {
        tptr = (tetrahedron *) point2tet(V[j]);
        if (((point *) tptr)[6] != insertpt) {
          // Found a unique vertex of the cavity.
          //setpointgeomtag(V[j], local_vcount);
          local_vcount++;
          setpoint2tet(V[j], (tetrahedron) (newtet.tet));
        }
        //sidx[j] = pointgeomtag(V[j]);
      } // j
    } // i, f_out
    
    // Set a handle for speeding point location.
    //recenttet = newtet;
    //setpoint2tet(insertpt, (tetrahedron) (newtet.tet));
    i = rand() % f_out;
    recenttet = * (triface *) fastlookup(cavebdrylist, i);
    // This is still an oldtet.
    fsymself(recenttet);
    fsymself(recenttet);
    setpoint2tet(insertpt, (tetrahedron) (recenttet.tet));

    for (i = 0; i < f_out; i++) {
      pcavetet = (triface *) fastlookup(cavebdrylist, i);
      oldtet = *pcavetet;

      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] == NULL) {
          // Find the adjacent face of this newtet.
          spintet = oldtet;
          while (1) {
            fnextself(spintet);
            if (!infected(spintet)) break;
          }
          fsym(spintet, neineitet);
          esymself(neineitet);
          bond(neightet, neineitet);
        }
        enextself(newtet);
        enextself(oldtet);
      } // j
    } // i
  } // fill cavity
  
  // C(p) is re-meshed successfully.

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

  cave_oldtet_list->restart();
  cavebdrylist->restart();

  return 1;
}

//============================================================================//
//                                                                            //
// 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.
  maketetrahedron2(&firsttet, pa, pb, pc, pd);
  //setvertices(firsttet, pa, pb, pc, pd);

  // Create four hull tetrahedra.
  maketetrahedron2(&tetopa, pb, pc, pd, dummypoint);
  //setvertices(tetopa, pb, pc, pd, dummypoint);
  maketetrahedron2(&tetopb, pc, pa, pd, dummypoint);
  //setvertices(tetopb, pc, pa, pd, dummypoint);
  maketetrahedron2(&tetopc, pa, pb, pd, dummypoint);
  //setvertices(tetopc, pa, pb, pd, dummypoint);
  maketetrahedron2(&tetopd, pb, pa, pc, dummypoint);
  //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));

  setpoint2tet(dummypoint, encode(tetopa));

  // 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(this, 10);
    }
  }
  if (i > 1) {
    // Swap to move the non-identical 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(this, 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-identical 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(this, 10);
    }
    ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], 
                       permutarray[i]);
  }
  if (i > 3) {
    // Swap to move the non-identical 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;

  ivf.bowywat = 1; // Use Bowyer-Watson algorithm
  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 = NULL;
    }
    ivf.iloc = (int) OUTSIDE;
    // Insert the vertex.
    if (!insert_vertex_bw(permutarray[i], &searchtet, &ivf)) {
      if (ivf.iloc == (int) ONVERTEX) {
        // The point already exists. Mark it and do nothing on it.
        swapvertex = org(searchtet);
        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) {
        // This should not happen by insert_point_bw().
        terminatetetgen(this, 2); // report a bug.
      } else if (ivf.iloc == (int) NONREGULAR) {
        // The point is non-regular. Skipped.
        if (b->verbose) {
          printf("  Point #%d is non-regular, skipped.\n",
                 pointmark(permutarray[i]));
        }
        setpointtype(permutarray[i], NREGULARVERTEX);
        nonregularcount++;
      }
    }
  }


  
  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 != NULL) {
      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 != NULL) {
      // Make sure that the subface has the ori as the segment.
      if (bdsegs[(3 + i) % 4].sh != NULL) {
        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 != NULL) {
      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 != NULL) {
      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]));
  }
  if (useinsertradius) {
    setfacetindex(flipfaces[3], getfacetindex(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 != NULL) {
      // Make sure that the subface has the ori as the segment.
      if (bdsegs[i].sh != NULL) {
        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 != NULL) {
      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 *) NULL) {

    // 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] == NULL) 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 == NULL) 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 NULL.                                              //
//                                                                            //
// 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 NULL, 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. They're needed in case we  //
// want to remove the new point immediately.                                  //
//                                                                            //
//============================================================================//

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 != NULL) && (splitseg->sh != NULL)) {
    // 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 == NULL) {
        *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 != NULL) && (splitseg->sh != NULL)) {
      splitseg->shver = 0;
      pa = sorg(*splitseg);
    } else {
      pa = sorg(*searchsh);
    }
    if (searchsh->sh != NULL) {
      // 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 == NULL) 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 == NULL) {
        // 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 = NULL;  // 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));
      }
      if (useinsertradius) {
        setfacetindex(newsh, getfacetindex(*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 != NULL) {
        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 == NULL) {
          *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 != NULL) {
          // 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));
    if (checkconstraints) {
      //area = areabound(*parysh);
      setareabound(newsh, areabound(*parysh));
    }
    if (useinsertradius) {
      setfacetindex(newsh, getfacetindex(*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 != NULL) {
      casin = casout;
      if (checkseg.sh != NULL) {
        // 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 inverse.
        }
        spivot(casin, neighsh);
        while (neighsh.sh != parysh->sh) {
          casin = neighsh;
          spivot(casin, neighsh);
        }
      }
      sbond1(newsh, casout);
      sbond1(casin, newsh);
    }
    if (checkseg.sh != NULL) {
      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 != NULL) {
    // 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 == NULL) {
      // Find the adjacent new subface at edge [b, p].
      pb = sdest(*parysh);
      neighsh = *parysh;
      while (1) {
        senextself(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == NULL) break;
        if (!smarktested(neighsh)) break;
        if (sdest(neighsh) != pb) sesymself(neighsh);
      }
      if (neighsh.sh != NULL) {
        // 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);
      }
    }
    spivot(*parysh, newsh); // The new subface [a, b, p].
    senext2self(newsh); // At edge [p, a].
    spivot(newsh, neighsh);
    if (neighsh.sh == NULL) {
      // Find the adjacent new subface at edge [p, a].
      pa = sorg(*parysh);
      neighsh = *parysh;
      while (1) {
        senext2self(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == NULL) break;
        if (!smarktested(neighsh)) break;
        if (sorg(neighsh) != pa) sesymself(neighsh);
      }
      if (neighsh.sh != NULL) {
        // 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);
      }
    }
  }

  if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL))
      || (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);
          }
          // Connect adjacent faces at two other edges of cavesh and neighsh.
          //   As a result, the two degenerated new faces are squeezed 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]. Squeeze 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 != NULL) && (splitseg->sh != NULL)) {
      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, NULL);
      setshvertices(bseg, insertpt, pb, NULL);
      setshellmark(aseg, shellmark(*splitseg));
      setshellmark(bseg, shellmark(*splitseg));
      if (checkconstraints) {
        setareabound(aseg, areabound(*splitseg));
        setareabound(bseg, areabound(*splitseg));
      }
      if (useinsertradius) {
        setfacetindex(aseg, getfacetindex(*splitseg));
        setfacetindex(bseg, getfacetindex(*splitseg));
      }

      // Connect [#, a]<->[a, p].
      senext2(*splitseg, boutseg); // Temporarily use boutseg.
      spivotself(boutseg);
      if (boutseg.sh != NULL) {
        senext2(aseg, aoutseg);
        sbond(boutseg, aoutseg);
      }
      // Connect [p, b]<->[b, #].
      senext(*splitseg, aoutseg);
      spivotself(aoutseg);
      if (aoutseg.sh != NULL) {
        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 squeezed. 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 != NULL) && (splitseg->sh != NULL)) 

    // 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 != NULL) && (splitseg->sh != NULL)) {
      // 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 NULL, 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 != NULL) {
    // '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;
    // 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, NULL);
    setshellmark(abseg, shellmark(*parentseg));
    if (checkconstraints) {
      setareabound(abseg, areabound(*parentseg));
    }
    if (useinsertradius) {
      setfacetindex(abseg, getfacetindex(*parentseg));
    }
    // Connect [#, a]<->[a, b].
    senext2(prevseg, adjseg1);
    spivotself(adjseg1);
    if (adjseg1.sh != NULL) {
      adjseg1.shver = 0;
      senextself(adjseg1);
      senext2(abseg, adjseg2);
      sbond(adjseg1, adjseg2);
    }
    // Connect [a, b]<->[b, #].
    senext(*parentseg, adjseg1);
    spivotself(adjseg1);
    if (adjseg1.sh != NULL) {
      adjseg1.shver = 0;
      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);
    if (parentsh->sh != NULL) {
      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 == NULL) {
          break; // It is possible there is only one facet.
        }
        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);
      }      
      // startsh is [p, b, #1], find the subface [a, p, #2].
      neighsh = startsh;
      while (1) {
        senext2self(neighsh);
        sspivot(neighsh, checkseg);
        if (checkseg.sh != NULL) {
          // It must be the segment [a, p].
          break;
        }
        spivotself(neighsh);
        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]
        // 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));
    }
    // 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.
      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);
        if (spinsh.sh == parentsh->sh) break;
        if (sorg(spinsh) != delpt) sesymself(spinsh);
      } // 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) {
        // 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 NULL). 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 indicates 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);
  if (ori > 0) {
    sesymself(*searchsh); // Reverse the face orientation.
  } else if (ori == 0.0) {
    // This case should not happen theoretically. But... 
    return UNKNOWN; 
  }

  // 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);
  }
  if (i == 3) {
    return UNKNOWN;
  }

  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 == NULL) {
      return OUTSIDE; // A hull edge.
    }
    // Adjust the edge orientation.
    if (sorg(neighsh) != sdest(*searchsh)) {
      sesymself(neighsh);
    }

    // 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, NULL);
    area_abc = sqrt(dot(n, n));

    facenormal(pb, pc, searchpt, n, 1, NULL);
    area_bcp = sqrt(dot(n, n));
    if ((area_bcp / area_abc) < b->epsilon) {
      area_bcp = 0; // Rounding.
    }

    facenormal(pc, pa, searchpt, n, 1, NULL);
    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, NULL);
      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) {
        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 the surface triangulation.        //
//                                                                            //
// The segment is given by the origin of 'searchsh' and 'endpt'.              //
//                                                                            //
// If an edge in T is found matching this segment, the segment is "locked"    //
// in T at the edge.  Otherwise, flip the first edge in T that the segment    //
// crosses. Continue the search from the flipped face.                        //
//                                                                            //
// This routine uses 'orisent3d' to determine the search direction. It uses   //
// 'dummypoint' as the 'lifted point' in 3d, and it assumes that it (dummy-   //
// point) lies above the 'searchsh' (w.r.t the Right-hand rule).              //
//                                                                            //
//============================================================================//

enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, 
  point endpt, int insertsegflag, int reporterrorflag, int chkencflag)
{
  face flipshs[2], neighsh;
  point startpt, pa, pb, pc, pd;
  enum interresult dir;
  enum {MOVE_AB, MOVE_CA} nextmove;
  REAL ori_ab, ori_ca, len;

  pc = NULL; // Avoid warnings from MSVC
  // The origin of 'searchsh' is fixed.
  startpt = sorg(*searchsh); 
  nextmove = MOVE_AB; // Avoid compiler warning.

  if (b->verbose > 2) {
    printf("      Scout segment (%d, %d).\n", pointmark(startpt),
           pointmark(endpt));
  }
  len = distance(startpt, 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;
    }


    // Round the results.
    if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) {
      ori_ab = 0.0;
    } else {
      ori_ab = orient3d(startpt, pb, dummypoint, endpt);
    }
    if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) {
      ori_ca = 0.0;
    } else {
      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 is collinear with edge [a, b].
            dir = ACROSSVERT;
            break;
          } else { // (00)
            // startpt == endpt. Not possible.
            terminatetetgen(this, 2);
          }
        }
      }
    }

    // Move 'searchsh' to the next face, keep the origin unchanged.
    if (nextmove == MOVE_AB) {
      if (chkencflag) {
        // Do not cross boundary.
        if (isshsubseg(*searchsh)) {
          return ACROSSEDGE; // ACROSS_SEG
        }
      }
      spivot(*searchsh, neighsh);
      if (neighsh.sh != NULL) {
        if (sorg(neighsh) != pb) sesymself(neighsh);
        senext(neighsh, *searchsh);
      } else {
        // This side (startpt->pb) is outside. It is caused by rounding error.
        // Try the next side, i.e., (pc->startpt).
        senext2(*searchsh, neighsh);
        if (chkencflag) {
          // Do not cross boundary.
          if (isshsubseg(neighsh)) {
            *searchsh = neighsh;
            return ACROSSEDGE; // ACROSS_SEG
          }
        }
        spivotself(neighsh);
        if (sdest(neighsh) != pc) sesymself(neighsh);
        *searchsh = neighsh;
      }
    } else { // MOVE_CA
      senext2(*searchsh, neighsh);
      if (chkencflag) {
        // Do not cross boundary.
        if (isshsubseg(neighsh)) {
          *searchsh = neighsh;
          return ACROSSEDGE; // ACROSS_SEG
        }
      }
      spivotself(neighsh);
      if (neighsh.sh != NULL) {
        if (sdest(neighsh) != pc) sesymself(neighsh);
        *searchsh = neighsh;
      } else {
        // The same reason as above. 
        // Try the next side, i.e., (startpt->pb).
        if (chkencflag) {
          // Do not cross boundary.
          if (isshsubseg(*searchsh)) {
            return ACROSSEDGE; // ACROSS_SEG
          }
        }
        spivot(*searchsh, neighsh);
        if (sorg(neighsh) != pb) sesymself(neighsh);
        senext(neighsh, *searchsh);
      }
    }
  } // while

  if (dir == SHAREEDGE) {
    if (insertsegflag) {
      // Insert the segment into the triangulation.
      face newseg;
      makeshellface(subsegs, &newseg);
      setshvertices(newseg, startpt, endpt, NULL);
      // Set the default segment marker.
      setshellmark(newseg, -1);
      ssbond(*searchsh, newseg);
      spivot(*searchsh, neighsh);
      if (neighsh.sh != NULL) {
        ssbond(neighsh, newseg);
      }
    }
    return dir;
  }

  if (dir == ACROSSVERT) {
    // A point is found collinear with this segment.
    if (reporterrorflag) {
      point pp = sdest(*searchsh);
      printf("PLC Error:  A vertex lies in a segment in facet #%d.\n",
             shellmark(*searchsh));
      printf("  Vertex:  [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]);
      printf("  Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt));
    }
    return dir;
  }

  if (dir == ACROSSEDGE) {
    // Edge [b, c] intersects with the segment.
    senext(*searchsh, flipshs[0]);
    if (isshsubseg(flipshs[0])) {
      if (reporterrorflag) {
        REAL P[3], Q[3], tp = 0, tq = 0;
        linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq);
        printf("PLC Error:  Two segments intersect at point (%g,%g,%g),", 
               P[0], P[1], P[2]);
        printf(" in facet #%d.\n", shellmark(*searchsh));
        printf("  Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc));
        printf("  Segment 2: [%d, %d]\n", pointmark(startpt),pointmark(endpt));
      }
      return dir; // ACROSS_SEG
    }
    // Flip edge [b, c], queue unflipped edges (for Delaunay checks).
    spivot(flipshs[0], flipshs[1]);
    if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]);
    flip22(flipshs, 1, 0);
    // The flip may create an inverted 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);
    if (ori_ab <= 0) {
      flipshpush(&(flipshs[0])); 
    } else if (ori_ca <= 0) {
      flipshpush(&(flipshs[1])); 
    }
    // Set 'searchsh' s.t. its origin is 'startpt'.
    *searchsh = flipshs[0];
  }

  return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, 
                       chkencflag);
}

//============================================================================//
//                                                                            //
// 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 != NULL) {
        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 != NULL) {
        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().              //
//                                                                            //
// All segments created here will have a default marker '-1'.  Some of these  //
//   segments will get their actual marker defined in 'edgemarkerlist'.       //
//                                                                            //
//============================================================================//

int tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist,
                            int holes, REAL* holelist)
{
  face searchsh, newsh, *parysh; 
  face newseg, *paryseg;
  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 1;
  } else 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, NULL);
      setshellmark(newseg, -1);
    }
    if (pointtype(pa) == VOLVERTEX) {
      setpointtype(pa, FACETVERTEX);
    }
    if (pointtype(pb) == VOLVERTEX) {
      setpointtype(pb, FACETVERTEX);
    }
    return 1;
  } else if (ptlist->objects == 3) {
    pa = * (point *) fastlookup(ptlist, 0);
    pb = * (point *) fastlookup(ptlist, 1);
    pc = * (point *) fastlookup(ptlist, 2);
  } else {
    // Calculate an above point of this facet.
    if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) {
      if (!b->quiet) {
        printf("Warning:  Unable to triangulate facet #%d. Skipped!\n",shmark);
      }
      return 0; // 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 != NULL)) {
    for (i = 0; i < in->numberoffacetconstraints; i++) {
      if (shmark == ((int) in->facetconstraintlist[i * 2])) {
        REAL 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), NULL);
      setshellmark(newseg, -1);
      ssbond(newsh, newseg);
      senextself(newsh);
    }
    return 1;
  }

  // Triangulate the facet. It may not success (due to rounding error, or
  //   incorrect input data), use 'caveencshlist' and 'caveencseglist' are
  //   re-used to store all the newly created subfaces and segments. So we
  //   can clean them if the triangulation is not successful.
  caveencshlist->newindex((void **) &parysh);
  *parysh = newsh;

  // 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, NULL, iloc, 1, 1);
      if (iloc != ((int) ONVERTEX)) { 
        // Point inserted successfully.
        if (pointtype(*ppt) == VOLVERTEX) {
          setpointtype(*ppt, FACETVERTEX);
        }
        // Save the set of new subfaces.
        for (j = 0; j < caveshbdlist->objects; j++) {
          // Get an old subface at edge [a, b].
          parysh = (face *) fastlookup(caveshbdlist, j);
          spivot(*parysh, searchsh); // The new subface [a, b, p].
          // Do not save a deleted new face (degenerated).
          if (searchsh.sh[3] != NULL) {
            caveencshlist->newindex((void **) &parysh);
            *parysh = searchsh;
          }
        }
        // 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 {
        // The facet triangulation is failed.
        break;
      }
    } 
  } // i
  puninfect(pa);
  puninfect(pb);
  puninfect(pc);

  if (i < ptlist->objects) {
    //The facet triangulation is failed. Clean the new subfaces.
    //  There is no new segment be created yet.
    if (!b->quiet) {
      printf("Warning:  Fail to triangulate facet #%d. Skipped!\n", shmark);
    }
    for (i = 0; i < caveencshlist->objects; i++) {
      parysh = (face *) fastlookup(caveencshlist, i);
      if (parysh->sh[3] != NULL) {
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
    caveencshlist->restart();
    return 0;
  }

  // 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 != (int) ONVERTEX) {
      // Not found due to roundoff errors. Do a brute-force search.
      bool bflag = false;
      subfaces->traversalinit();
      searchsh.sh = shellfacetraverse(subfaces);
      while (searchsh.sh != NULL) {
        // Only search the subface in the same facet.
        if (shellmark(searchsh) == shmark) {
          if ((point) searchsh.sh[3] == cons[0]) {
            searchsh.shver = 0; bflag = true; //break;
          } else if ((point) searchsh.sh[4] == cons[0]) {
            searchsh.shver = 2; bflag = true; //break;
          } else if ((point) searchsh.sh[5] == cons[0]) {
            searchsh.shver = 4; bflag = true; //break;
          }
        }
        if (bflag) {
          // [2019-12-03] The subface is not guaranteed to be coplanar,
          //    only use "shmark" is not enough.
          point pa = sorg(searchsh);
          point pb = sdest(searchsh);
          point pc = sapex(searchsh);
          REAL chkori = orient3d(pa, pb, pc, cons[1]);
          if (chkori != 0.0) {
            REAL len = distance(pa, pb);
            len += distance(pb, pc);
            len += distance(pc, pa);
            len /= 3.0;
            REAL len3 = len * len * len;
            REAL eps = fabs(chkori) / len3;
            if (eps < 1e-5) {
              break; // They are almost coplanar.
            }
          } else {
            break;
          }
          bflag = false; // not this subface.
        }
        searchsh.sh = shellfacetraverse(subfaces);
	  }
      //if (searchsh.sh == NULL) {
      //  // Failed to find a subface containing vertex cons[0].
      //}
    }
    if (searchsh.sh != NULL) {
      // Recover the segment. Some edges may be flipped.
      if (sscoutsegment(&searchsh, cons[1], 1, 1, 0) != SHAREEDGE) {
        break; // Fail to recover a segment.
      }
      // Save this newseg.
      sspivot(searchsh, newseg);
      caveencseglist->newindex((void **) &paryseg);
      *paryseg = newseg;
      if (flipstack != NULL) {
        // Recover locally Delaunay edges.
        lawsonflip();
      }
    } else {
      break; // Failed to find a segment.
    }
  } // i

  if (i < conlist->objects) {
    if (!b->quiet) {
      printf("Warning:  Fail to recover a segment in facet #%d. Skipped!\n",
             shmark);
    }
    for (i = 0; i < caveencshlist->objects; i++) {
      parysh = (face *) fastlookup(caveencshlist, i);
      if (parysh->sh[3] != NULL) {
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
    for (i = 0; i < caveencseglist->objects; i++) {
      paryseg = (face *) fastlookup(caveencseglist, i);
      if (paryseg->sh[3] != NULL) {
        shellfacedealloc(subsegs, paryseg->sh);
      }
    }
    caveencshlist->restart();
    caveencseglist->restart();
    return 0;
  }

  // Remove exterior and hole triangles.
  scarveholes(holes, holelist);

  caveencshlist->restart();
  caveencseglist->restart();
  return 1;
}

//============================================================================//
//                                                                            //
// 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 = NULL, *newlinkitem, *f1, *f2;
  face *facperverlist, sface;
  face subsegloop, testseg;
  point torg, tdest;
  REAL ori1, ori2; //, ori3;
  REAL n1[3], n2[3];
  REAL cosang, ang, ang_tol;
  int *idx2faclist;
  int idx, k, m;

  if (b->verbose > 1) {
    printf("  Unifying segments.\n");
  }
  // The limit dihedral angle that two facets are not overlapping.
  //ang_tol = b->facet_overlap_ang_tol / 180.0 * PI;
  //if (ang_tol < 0.0) ang_tol = 0.0;

  // Create a mapping from vertices to subfaces.
  makepoint2submap(subfaces, idx2faclist, facperverlist);


  subsegloop.shver = 0;
  subsegs->traversalinit();
  subsegloop.sh = shellfacetraverse(subsegs);
  while (subsegloop.sh != (shellface *) NULL) {
    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] == NULL) continue;
      // Search the edge torg->tdest.
      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 = facedihedral(torg, tdest, sapex(f1->ss), sapex(f2->ss));
          ori2 = facedihedral(torg, tdest, sapex(f1->ss), sapex(sface));
          if (ori1 >= ori2) {
            break; // insert this face between f1 and f2.
          }
          // Go to the next item;
          f1 = f2;
        } // for (m = 0; ...)
        //if (sface.sh[3] != NULL) {
          // 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;
        // Add this face to link if it is not deleted.
        //if (sface.sh[3] != NULL) {
          // Add this face into link.
          newlinkitem = (badface *) flippool->alloc();
          newlinkitem->ss = sface;
          newlinkitem->nextitem = NULL;
          f1->nextitem = newlinkitem;
        //}
      } else {
        // The first face.
        newlinkitem = (badface *) flippool->alloc();
        newlinkitem->ss = sface;
        newlinkitem->nextitem = NULL;
        facelink = newlinkitem;
      }
    } // for (k = idx2faclist[idx]; ...)


    // 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] != NULL)) {
        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;
        // Calculate the dihedral angle between the two facet.
        facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL);
        facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL);
        cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
        // Rounding.
        if (cosang > 1.0) cosang = 1.0;
        else if (cosang < -1.0) cosang = -1.0;
        ang = acos(cosang);
        //if (ang < ang_tol) {
        //  // Two facets are treated as overlapping each other.
        //  report_overlapping_facets(&(f1->ss), &(f2->ss), ang);
        //} else {
          // Record the smallest input dihedral angle.
          if (ang < minfacetdihed) {
            minfacetdihed = ang;
          }
          sbond1(f1->ss, f2->ss);
        //}
        f1 = f2;
      }
    }

    flippool->restart();

    // Are there length constraints?
    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
      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;
}

//============================================================================//
//                                                                            //
// identifyinputedges()    Identify input edges.                              //
//                                                                            //
// A set of input edges is provided in the 'in->edgelist'.  We find these     //
// edges in the surface mesh and make them segments of the mesh.              //
//                                                                            //
// It is possible that an input edge is not in any facet, i.e.,it is a float- //
// segment inside the volume.                                                 //
//                                                                            //
//============================================================================//

void tetgenmesh::identifyinputedges(point *idx2verlist)
{
  face* shperverlist;
  int* idx2shlist;
  face searchsh, neighsh;
  face segloop, checkseg, newseg;
  point checkpt, pa = NULL, pb = NULL;
  int *endpts;
  int edgemarker;
  int idx, i, j;

  int e1, e2;
  REAL len;

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

  // Construct a map from points to subfaces.
  makepoint2submap(subfaces, idx2shlist, shperverlist);

  // Process the set of input edges.
  for (i = 0; i < in->numberofedges; i++) {
    endpts = &(in->edgelist[(i << 1)]);
    if (endpts[0] == endpts[1]) {
      if (!b->quiet) {
        printf("Warning:  Edge #%d is degenerated. Skipped.\n", i);
      }
      continue; // Skip a degenerated edge.
    } else if (dupverts > 0l) {
      // Replace duplicated vertices.
      for (j = 0; j < 2; j++) {
        checkpt = idx2verlist[endpts[j]];
        if (pointtype(checkpt) == DUPLICATEDVERTEX) {
          point meshpt = point2ppt(checkpt);
          endpts[j] = pointmark(meshpt);
        }
      }
    }
    // Recall that all existing segments have a default marker '-1'. 
    // We assign all identified segments a default marker '-2'.
    edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2;

    // Find a face contains the edge.
    newseg.sh = NULL;
    searchsh.sh = NULL;
    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

    if (searchsh.sh != NULL) {
      // Check if this edge is already a segment of the mesh.
      sspivot(searchsh, checkseg);
      if (checkseg.sh != NULL) {
        // This segment already exist.
        newseg = checkseg;
      } else {
        // Create a new segment at this edge.
        pa = sorg(searchsh);
        pb = sdest(searchsh);
        makeshellface(subsegs, &newseg);
        setshvertices(newseg, pa, pb, NULL);
        ssbond(searchsh, newseg);
        spivot(searchsh, neighsh);
        if (neighsh.sh != NULL) {
          ssbond(neighsh, 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]];
      if (pa == pb) {
        if (!b->quiet) {
          printf("Warning:  Edge #%d is degenerated. Skipped.\n", i);
        }
        continue;
      }
      // Check if segment [a,b] already exists.
      // TODO: Change the brute-force search. Slow!
      point *ppt;
      subsegs->traversalinit();
      segloop.sh = shellfacetraverse(subsegs);
      while (segloop.sh != NULL) {
        ppt = (point *) &(segloop.sh[3]);
        if (((ppt[0] == pa) && (ppt[1] == pb)) ||
            ((ppt[0] == pb) && (ppt[1] == pa))) {
          // Found!
          newseg = segloop; 
          break;
        }
        segloop.sh = shellfacetraverse(subsegs);
      }
      if (newseg.sh == NULL) {
        makeshellface(subsegs, &newseg);
        setshvertices(newseg, pa, pb, NULL);
      }
    }

    setshellmark(newseg, edgemarker);

    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
      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;
}

//============================================================================//
//                                                                            //
// mergefacets()    Merge adjacent facets.                                    //
//                                                                            //
//============================================================================//

void tetgenmesh::mergefacets()
{
  face parentsh, neighsh, neineish;
  face segloop;
  point pa, pb, pc, pd;
  REAL n1[3], n2[3];
  REAL cosang, cosang_tol;

  // Allocate an array to save calcaulated dihedral angles at segments.
  arraypool *dihedangarray = new arraypool(sizeof(double), 10);
  REAL *paryang = NULL;

  // First, remove coplanar segments.
  // The dihedral angle bound for two different facets.
  cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI);

  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Only remove a segment if it has a marker '-1'.
    if (shellmark(segloop) != -1) {
      segloop.sh = shellfacetraverse(subsegs);
      continue;
    }
    spivot(segloop, parentsh);
    if (parentsh.sh != NULL) {
      spivot(parentsh, neighsh);
      if (neighsh.sh != NULL) {
        spivot(neighsh, neineish);
        if (neineish.sh == parentsh.sh) {
          // Exactly two subfaces at this segment.
          // Only merge them if they have the same boundary marker.
          if (shellmark(parentsh) == shellmark(neighsh)) {
            pa = sorg(segloop);
            pb = sdest(segloop);
            pc = sapex(parentsh);
            pd = sapex(neighsh);
            // Calculate the dihedral angle at the segment [a,b].
            facenormal(pa, pb, pc, n1, 1, NULL);
            facenormal(pa, pb, pd, n2, 1, NULL);
            cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
            if (cosang < cosang_tol) {
              ssdissolve(parentsh);
              ssdissolve(neighsh);
              shellfacedealloc(subsegs, segloop.sh);
              // Add the edge to flip stack.
              flipshpush(&parentsh);
            } else {
              // Save 'cosang' to avoid re-calculate it.
              // Re-use the pointer at the first segment.
              dihedangarray->newindex((void **) &paryang);
              *paryang = cosang;
              segloop.sh[6] = (shellface) paryang;
            }
          }
        } // if (neineish.sh == parentsh.sh)
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // Second, remove ridge segments at small angles.
  // The dihedral angle bound for two different facets.
  cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI);
  REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI);
  face shloop;
  face seg1, seg2;
  REAL cosang1, cosang2;
  int i, j;

  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      if (isshsubseg(shloop)) {
        senext(shloop, neighsh);
        if (isshsubseg(neighsh)) {
          // Found two segments sharing at one vertex.
          // Check if they form a small angle.
          pa = sorg(shloop);
          pb = sdest(shloop);
          pc = sapex(shloop);
          for (j = 0; j < 3; j++) n1[j] = pa[j] - pb[j];
          for (j = 0; j < 3; j++) n2[j] = pc[j] - pb[j];
          cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
          if (cosang > cosang_tol) {
            // Found a small angle.
            segloop.sh = NULL;
            sspivot(shloop, seg1);
            sspivot(neighsh, seg2);
            if (seg1.sh[6] != NULL) {
              paryang = (REAL *) (seg1.sh[6]);
              cosang1 = *paryang;
            } else {
              cosang1 = 1.0; // 0 degree;
            }
            if (seg2.sh[6] != NULL) {
              paryang = (REAL *) (seg2.sh[6]);
              cosang2 = *paryang;
            } else {
              cosang2 = 1.0; // 0 degree;
            }
            if (cosang1 < cosang_sep_tol) {
              if (cosang2 < cosang_sep_tol) {
                if (cosang1 < cosang2) {
                  segloop = seg1;
                } else {
                  segloop = seg2;
                }
              } else {
                segloop = seg1;
              }
            } else {
              if (cosang2 < cosang_sep_tol) {
                segloop = seg2;
              }
            }
            if (segloop.sh != NULL) {
              // Remove this segment.
              segloop.shver = 0;
              spivot(segloop, parentsh);
              spivot(parentsh, neighsh);
              ssdissolve(parentsh);
              ssdissolve(neighsh);
              shellfacedealloc(subsegs, segloop.sh);
              // Add the edge to flip stack.
              flipshpush(&parentsh);
              break;
            }
          }
        } // if (isshsubseg)
      } // if (isshsubseg)
      senextself(shloop);
    }
    shloop.sh = shellfacetraverse(subfaces);
  }

  delete dihedangarray;


  if (flipstack != NULL) {
    lawsonflip(); // Recover Delaunayness.
  }
}

//============================================================================//
//                                                                            //
// 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.
    // If in->facetmarklist is NULL, use the default marker -1. 
    triangulate(in->facetmarkerlist ? in->facetmarkerlist[shmark - 1] : -1,
                ptlist, conlist, f->numberofholes, f->holelist);

    // Clear working lists.
    ptlist->restart();
    conlist->restart();
  }


  // Remove redundant segments and build the face links.
  unifysegments();
  if (in->numberofedges > 0) {
    // There are input segments. Insert them.
    identifyinputedges(idx2verlist);
  }
  if (!b->diagnose && !b->nomergefacet && !b->nobisect) { // No -d -M -Y
    // Merge coplanar facets.
    mergefacets();
  }

  // Mark all segment vertices to be RIDGEVERTEX.
  face segloop;
  point *ppt;
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != NULL) {
    ppt = (point *) &(segloop.sh[3]);
    for (i = 0; i < 2; i++) {
      setpointtype(ppt[i], RIDGEVERTEX);
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  if (b->object == tetgenbehavior::STL) {
    // Remove redundant vertices (for .stl input mesh).
    jettisonnodes();
    // Update the number of input vertices.
    in->numberofpoints = points->items;
  }

  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;
}

//                                                                            //
//                                                                            //
//== surface_cxx =============================================================//

//== constrained_cxx =========================================================//
//                                                                            //
//                                                                            //

//============================================================================//
//                                                                            //
// 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 {
      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.
      if (nonconvex) {
        return ACROSSFACE; // return ACROSSSUB; // Hit a bounday.
      } else {
        terminatetetgen(this, 2);
      }
    }

    // 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.
          if (randomnation(2)) {
            nextmove = HMOVE;
          } else {
            nextmove = RMOVE;
          }
        }
      } else {
        if (lori > 0) {
          // Two tets, below horizon and below left, are viable.
          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.
          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);
    }
    if (org(*searchtet) != pa) {
      terminatetetgen(this, 2);
    }
    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 NULL, //
// 'searchtet' returns the edge or face. If 'refpt' is not NULL, it returns   //
// a vertex which encroaches upon this edge, and 'searchtet' returns a tet    //
// which containing 'refpt'.                                                  //
//                                                                            //
// The parameter 'sedge' is used to report self-intersection. It is the       //
// whose endpoints are 'startpt' and 'endpt'. It must not be a NULL.          //
//                                                                            //
//============================================================================//

enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt,point endpt,
  face *sedge, 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) {
      if (issubseg(*searchtet)) {
        //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
        terminatetetgen(this, 3);
      }
      return SHAREEDGE;
    } else {
      // A point is on the path.
      //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
      terminatetetgen(this, 3);
      return 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)) {
      //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
      terminatetetgen(this, 3);
    }
  } else if (dir == ACROSSFACE) {
    if (checksubfaceflag) {
      // Check whether a segment and a subface are intersecting.
      if (issubface(*searchtet)) {
        //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
        terminatetetgen(this, 3);
      }
    }
  } else {
    terminatetetgen(this, 2);
  }

  if (refpt == NULL) {
    // 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, NULL);
  *refpt = pa;
  pb = dest(*searchtet);
  ang = interiorangle(pb, startpt, endpt, NULL);
  if (ang > angmax) {
    angmax = ang;
    *refpt = pb;
  }
  pc = apex(*searchtet);
  ang = interiorangle(pc, startpt, endpt, NULL);
  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);


    // Stop if we meet 'endpt'.
    if (pd == endpt) break;

    ang = interiorangle(pd, startpt, endpt, NULL);
    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;
        }
      }
    } else if (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);
      }
      eprev(neightet, *searchtet); 
      // dest(*searchtet) lies on the segment.
      //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
      terminatetetgen(this, 3);
      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)) {
        //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
        terminatetetgen(this, 3);
      }  
    } else if (dir == ACROSSFACE) {
      if (checksubfaceflag) {
        // Check whether a segment and a subface are intersecting.
        if (issubface(*searchtet)) {
          //report_selfint_edge(startpt, endpt, sedge, searchtet, dir);
          terminatetetgen(this, 3);
        }
      }
    } else {
      terminatetetgen(this, 2);
    }

  } // 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 = NULL;
  }


  *searchtet = reftet;
  return dir;
}

//============================================================================//
//                                                                            //
// getsteinerpointonsegment()    Get a Steiner point on a segment.            //
//                                                                            //
// Return '1' if 'refpt' lies on an adjacent segment of this segment. Other-  //
// wise, return '0'.                                                          //
//                                                                            //
//============================================================================//

int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt)
{
  point ei = sorg(*seg);
  point ej = sdest(*seg);
  int adjflag = 0, i;

  if (refpt != NULL) {
    REAL L, L1, t;
  
    if (pointtype(refpt) == FREESEGVERTEX) {
      face parentseg;
      sdecode(point2sh(refpt), parentseg);
      int sidx1 = getfacetindex(parentseg);
      point far_pi = segmentendpointslist[sidx1 * 2];
      point far_pj = segmentendpointslist[sidx1 * 2 + 1];
      int sidx2 = getfacetindex(*seg);
      point far_ei = segmentendpointslist[sidx2 * 2];
      point far_ej = segmentendpointslist[sidx2 * 2 + 1];
      if ((far_pi == far_ei) || (far_pj == far_ei)) {
        // Create a Steiner point at the intersection of the segment
        //   [far_ei, far_ej] and the sphere centered at far_ei with 
        //   radius |far_ei - refpt|.
        L = distance(far_ei, far_ej);
        L1 = distance(far_ei, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]);
        }
        adjflag = 1;
      } else if ((far_pi == far_ej) || (far_pj == far_ej)) {
        L = distance(far_ei, far_ej);
        L1 = distance(far_ej, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]);
        }
        adjflag = 1;
      } else {
        // Cut the segment by the projection point of refpt.
        projpt2edge(refpt, ei, ej, steinpt);
      }
    } else {
      // Cut the segment by the projection point of refpt.
      projpt2edge(refpt, ei, ej, steinpt);
    }

    // Make sure that steinpt is not too close to ei and ej.
    L = distance(ei, ej);
    L1 = distance(steinpt, ei);
    t = L1 / L;
    if ((t < 0.2) || (t > 0.8)) {
      // Split the point at the middle.
      for (i = 0; i < 3; i++) {
        steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
      }
    }
  } else {
    // Split the point at the middle.
    for (i = 0; i < 3; i++) {
      steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
    }
  }


  return adjflag;
}



//============================================================================//
//                                                                            //
// 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.sloc = (int) ONEDGE; // on 'sseg'.
  ivf.sbowywat = 1; // Use Bowyer-Watson insertion.
  ivf.assignmeshsize = b->metric;
  ivf.smlenflag = useinsertradius; // Return the closet mesh vertex.

  // 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 != NULL) {
      continue; // Not a missing segment.
    }

    // Search the segment.
    dir = scoutsegment(sorg(sseg), sdest(sseg), &sseg,&searchtet,&refpt,NULL);

    if (dir == SHAREEDGE) {
      // Found this segment, 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 ((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--;
          if (useinsertradius) {
            //save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen);
          } 
        } else {
          if (ivf.iloc == (int) NEARVERTEX) {
            // The new point (in the segment) is very close to an existing 
            //   vertex -- a small feature is detected.
            point nearpt = org(searchtet);
            if (pointtype(nearpt) == FREESEGVERTEX) {
              face parentseg;
              sdecode(point2sh(nearpt), parentseg);
              point p1 = farsorg(sseg);
              point p2 = farsdest(sseg);
              point p3 = farsorg(parentseg);
              point p4 = farsdest(parentseg);
              printf("Two segments are very close to each other.\n");
              printf("  Segment 1: [%d, %d] #%d\n", pointmark(p1),
                     pointmark(p2), shellmark(sseg));
              printf("  Segment 2: [%d, %d] #%d\n", pointmark(p3),
                     pointmark(p4), shellmark(parentseg));
              terminatetetgen(this, 4);
            } else {
              terminatetetgen(this, 2);
            }
          } else if (ivf.iloc == (int) ONVERTEX) {
            // The new point (in the segment) is coincident with an existing 
            //   vertex -- a self-intersection is detected.
            eprevself(searchtet);
            //report_selfint_edge(sorg(sseg), sdest(sseg), &sseg, &searchtet,
            //                    ACROSSVERT);
            terminatetetgen(this, 3);
          } else {
            // An unknown case. Report a bug.
            terminatetetgen(this, 2);
          }
        }
      } else {
        // An unknown case. Report a bug.
        terminatetetgen(this, 2);
      }
    }
  } // 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 parameter 'shflag' indicates whether 'searchsh' is a boundary face or  //
// not. It is possible that 'searchsh' is a temporarily subface that is used  //
// as a cavity boundary face.                                                 //
//                                                                            //
//============================================================================//

int tetgenmesh::scoutsubface(face* searchsh, triface* searchtet, int shflag)
{
  point pa = sorg(*searchsh);
  point pb = sdest(*searchsh);

  // Get a tet whose origin is a.
  point2tetorg(pa, *searchtet);
  // Search the edge [a,b].
  enum interresult dir = finddirection(searchtet, pb);
  if (dir == ACROSSVERT) {
    // Check validity of a PLC.
    if (dest(*searchtet) != pb) {
	  if (shflag) {
        // A vertex lies on the search edge. 
	    //report_selfint_edge(pa, pb, searchsh, searchtet, dir);
        terminatetetgen(this, 3);
	  } else {
	    terminatetetgen(this, 2);
	  }
    }
	int t1ver;
    // The edge exists. Check if the face exists.
    point pc = sapex(*searchsh);
    // Searchtet holds edge [a,b]. Search a face with apex c.
    triface 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 1; 
        } else {
          terminatetetgen(this, 2);
        }
      }
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    }
  }

  return 0;
}

//============================================================================//
//                                                                            //
// 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) {
		  // Report a PLC problem.
		  //report_selfint_edge(pa, pb, missh, &searchtet, dir);
          terminatetetgen(this, 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 == NULL) || !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 == NULL) {
          // 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, neightet;
  face oldsh, searchsh, *parysh;
  face neighseg;
  point pa, pb, pc, pd, pe;
  REAL ori;
  int types[2], poss[4];
  int searchflag, interflag;
  int t1ver;
  int i, j;

  searchflag = 0;

  // Search the first new subface to fill the region.
  for (i = 0; i < missingshbds->objects && !searchflag; i++) {
    parysh = (face *) fastlookup(missingshbds, i);
    sspivot(*parysh, neighseg);
    sstpivot1(neighseg, searchtet);
    if (org(searchtet) != sorg(*parysh)) {
      esymself(searchtet);
    }
    spintet = searchtet;
    while (1) {
      if (pmarktested(apex(spintet))) {
        // A possible interior face.
        neightet = spintet;
        oldsh = *parysh;
		// Try to recover an interior edge.
		for (j = 0; j < 2; j++) {
		  enextself(neightet);
          if (!issubseg(neightet)) {
			if (j == 0) {
              senext(oldsh, searchsh);
            } else {
              senext2(oldsh, searchsh);
              sesymself(searchsh);
		      esymself(neightet);
            }
            // Calculate a lifted point. 
			pa = sorg(searchsh);
            pb = sdest(searchsh);
            pc = sapex(searchsh);
            pd = dest(neightet);
            calculateabovepoint4(pa, pb, pc, pd);
            // The lifted point must lie above 'searchsh'.
			ori = orient3d(pa, pb, pc, dummypoint);
			if (ori > 0) {
		      sesymself(searchsh);
		      senextself(searchsh);
		    } else if (ori == 0) {
		      terminatetetgen(this, 2);
		    }
			if (sscoutsegment(&searchsh,dest(neightet),0,0,1)==SHAREEDGE) {
              // Insert a temp segment to protect the recovered edge.
              face tmpseg;
              makeshellface(subsegs, &tmpseg);
              ssbond(searchsh, tmpseg);
              spivotself(searchsh);
              ssbond(searchsh, tmpseg);
              // Recover locally Delaunay edges.
              lawsonflip();
              // Delete the tmp segment.
              spivot(tmpseg, searchsh);
              ssdissolve(searchsh);
              spivotself(searchsh);
              ssdissolve(searchsh);
              shellfacedealloc(subsegs, tmpseg.sh);
			  searchflag = 1;
            } else {
              // Undo the performed flips.
              if (flipstack != NULL) {
                lawsonflip();
              }
            }
            break;
          } // if (!issubseg(neightet))
		} // j
        if (searchflag) break;
      } // if (pmarktested(apex(spintet)))
      fnextself(spintet);
      if (spintet.tet == searchtet.tet) break;
    }
  } // i

  if (searchflag) {
	// Remove faked segments.
    face checkseg;
    // Remark: We should not use the array 'missingshbds', since the flips may
    //   change the subfaces. We search them from the subfaces in R.
    for (i = 0; i < missingshs->objects; i++) {
      parysh = (face *) fastlookup(missingshs, i);
      oldsh = *parysh;
      for (j = 0; j < 3; j++) {
        if (isshsubseg(oldsh)) {
          sspivot(oldsh, checkseg);
          if (sinfected(checkseg)) {
            // It's a faked segment. Delete it.
            sstpivot1(checkseg, searchtet);
            spintet = searchtet;
            while (1) {
              tssdissolve1(spintet);
              fnextself(spintet);
              if (spintet.tet == searchtet.tet) break;
            }
            shellfacedealloc(subsegs, checkseg.sh);
            ssdissolve(oldsh);
          }
        }
        senextself(oldsh);
      } // j
    }

    fillregioncount++;

    return 0;
  } // if (i < missingshbds->objects)

  searchflag = -1;

  for (j = 0; j < missingshbds->objects && (searchflag == -1); 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, NULL, 1, types, poss);
            if (interflag > 0) { 
              if (interflag == 2) {
                // They intersect at a single point.
				if ((types[0] == (int) ACROSSFACE) ||
				    (types[0] == (int) ACROSSEDGE)) {
                  // Go to the crossing edge [d,e,#,#].
                  edestoppo(spintet, crosstet); // // [d,e,#,#].
                  if (issubseg(crosstet)) {
				    // It is a segment. Report a PLC problem.
				    //report_selfint_face(pa, pb, pc, parysh, &crosstet,
					//                    interflag, types, poss);
                    terminatetetgen(this, 3);
                  } else {
				    triface chkface = crosstet;
					while (1) {
					  if (issubface(chkface)) break;
					  fsymself(chkface);
					  if (chkface.tet == crosstet.tet) break;
					}
					if (issubface(chkface)) {
					  // Two subfaces are intersecting.
                      //report_selfint_face(pa, pb, pc, parysh, &chkface,
					  //                  interflag, types, poss);
                      terminatetetgen(this, 3);
					}
				  }
                  // Adjust the edge such that d lies below [a,b,c].
                  ori = orient3d(pa, pb, pc, pd);
                  if (ori < 0) {
                    esymself(crosstet);
                  }
                  searchflag = 1;                  
                } else {
                  // An improper intersection type, ACROSSVERT, TOUCHFACE,
				  //   TOUCHEDGE, SHAREVERT, ...
                  // Maybe it is due to a PLC problem.
				  //report_selfint_face(pa, pb, pc, parysh, &crosstet,
				  //	                  interflag, types, poss);
                  terminatetetgen(this, 3);
                }
              }
              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, chkface, *parytet;
  face *parysh = NULL;
  point pa, pd, pe, *parypt;
  bool testflag, invalidflag;
  int intflag, 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 crossing edge, d lies below R.
  //   NEITHER d NOR e is a vertex of R (!pmarktested). 
  for (i = 0; i < crossedges->objects && !invalidflag; 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.,to check if one
		  //   of the tetrahedron at this edge has been 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);
              intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), 
                          sapex(*parysh), pe, pa, NULL, 1, types, poss);
              if (intflag > 0) {
                // Found intersection. 'a' lies below R.
				if (intflag == 2) {
                  enext(spintet, neightet);
				  if ((types[0] == (int) ACROSSFACE) || 
                      (types[0] == (int) ACROSSEDGE)) {
                    // Only this case is valid.
                  } else {
                    // A non-valid intersection. Maybe a PLC problem.
                    invalidflag = 1;
                  }
				} else {
				  // Coplanar intersection. Maybe a PLC problem.
				  invalidflag = 1;
				}
                break;
              }
              intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), 
			              sapex(*parysh), pa, pd, NULL, 1, types, poss);
              if (intflag > 0) {
                // Found intersection. 'a' lies above R.
				if (intflag == 2) {
                  eprev(spintet, neightet);
				  if ((types[0] == (int) ACROSSFACE) || 
                      (types[0] == (int) ACROSSEDGE)) {
					// Only this case is valid.
                  } else {
				    // A non-valid intersection. Maybe a PLC problem.
                    invalidflag = 1;
                  }
				} else {
				  // Coplanar intersection. Maybe a PLC problem.
				  invalidflag = 1;
				}
                break;
              }
            } // k
            if (k < missingshs->objects) {
              // Found a pair of triangle - edge intersection.
              if (invalidflag) {
                break; // the while (1) loop
              }
              // Adjust the edge direction, so that its origin lies below R,
              //   and its destination lies above R.
              esymself(neightet);
              // This edge may be a segment.
              if (issubseg(neightet)) {
				//report_selfint_face(sorg(*parysh), sdest(*parysh),
                //  sapex(*parysh),parysh,&neightet,intflag,types,poss);
                terminatetetgen(this, 3);
              }
			  // Check if it is an edge of a subface.
			  chkface = neightet;
              while (1) {
                if (issubface(chkface)) break;
                fsymself(chkface);
                if (chkface.tet == neightet.tet) break;
              }
              if (issubface(chkface)) {
                // Two subfaces are intersecting.
                //report_selfint_face(sorg(*parysh), sdest(*parysh),
                //  sapex(*parysh),parysh,&chkface,intflag,types,poss);
                terminatetetgen(this, 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;
              break; // the while (1) loop
            } // if (k == missingshs->objects)
          } // if (testflag)
	    } 
      } // if (pa != dummypoint)
      // Go to the next crossing tet.
      fnextself(spintet);
      if (spintet.tet == searchtet->tet) break;
    } // while (1)
  } // i

  // Unmark all marked edges.
  for (i = 0; i < crossedges->objects; i++) {
    searchtet = (triface *) fastlookup(crossedges, i);
    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;
  }

  if (b->verbose > 2) {
    printf("      Formed cavity: %ld (%ld) cross tets (edges).\n", 
           crosstets->objects, crossedges->objects);
  }

  // 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;
  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 = NULL;
  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 = NULL;
  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 != NULL) break;
  }

  // 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, NULL, NULL, &ivf);
  }

  if (b->verbose > 2) {
    printf("      Identifying %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 = NULL; 
      if (scoutsubface(&tmpsh, &searchtet, 0)) { // shflag = 0
        // 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 temporary 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, NULL, NULL, &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 != NULL) {
      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 != NULL)
  } // j

  if (crossedge != NULL) {
    // 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,NULL,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);
                if (ori < 0) {
                  fsymself(toptet);
                  pa = org(toptet);
                  pb = dest(toptet);
                } else if (ori == 0) {
                  terminatetetgen(this, 2);
                }
                // 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 = NULL;
      // 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);
      // 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) {
            terminatetetgen(this, 2); // 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) {
          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 {
                // The 'bottet' is not inside the cavity! 
                terminatetetgen(this, 2); // Check this case
              }
            } 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!.
                    terminatetetgen(this, 2); //assert(0);
                  }
                  break; // Not matched
                }
                fsymself(bottet);
              }
            } // 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));
        }
        if (useinsertradius) {
          setfacetindex(newsh, getfacetindex(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 == NULL) {
            // 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 != NULL) {
                // It's a bdry edge of R.
                // 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 = NULL;
                }
                spivot(oldsh, casout);
                if (casout.sh != NULL) {
                  casin = casout;
                  if (checkseg.sh != NULL) {
                    // 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 != NULL) {
                  ssbond(newsh, checkseg);
                }
                break;
              } // if (checkseg.sh != NULL)
              // (2) Second check if this side is an interior edge of R.
              tspivot(searchtet, neighsh);
              if (neighsh.sh != NULL) {
                // Found an adjacent subface of newsh (an interior edge).
                sbond(newsh, neighsh);
                break;
              }
              fnextself(searchtet);
            } // while (1)
          } // if (casout.sh == NULL)
          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 != NULL) {
        // 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. 
        parysh = NULL;  // Avoid a warning in MSVC
        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 {
          terminatetetgen(this, 2); //assert(0);
        }
      } else {
        //terminatetetgen(this, 2); // Report a bug
      }
    }

    midfaces->restart();
  } else {
    mflag = true;
  } 

  // Delete the temp subfaces.
  for (j = 0; j < 2; j++) {
    cavshells = (j == 0 ? topshells : botshells);
    if (cavshells != NULL) {
      for (i = 0; i < cavshells->objects; i++) {
        parysh = (face *) fastlookup(cavshells, i);
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
  }

  topshells->restart();
  if (botshells != NULL) {
    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]) != NULL) {
      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]) != NULL) {
      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] != NULL) {
            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);
      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 != NULL) {
      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 != NULL) {
      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 != NULL) {
    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);
    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.
  if (crosstets->objects > 0) {
    recenttet = * (triface *) fastlookup(crosstets, 0);
  }

  // Delete faked segments.
  for (i = 0; i < missingshbds->objects; i++) {
    parysh = (face *) fastlookup(missingshbds, i);
    sspivot(*parysh, checkseg);
    if (checkseg.sh[3] != NULL) {
      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 = NULL;
      }
    }
  } // i

  // Delete new tets.
  for (i = 0; i < topnewtets->objects; i++) {
    parytet = (triface *) fastlookup(topnewtets, i);
    tetrahedrondealloc(parytet->tet);
  }

  if (botnewtets != NULL) {
    for (i = 0; i < botnewtets->objects; i++) {
      parytet = (triface *) fastlookup(botnewtets, i);
      tetrahedrondealloc(parytet->tet);
    }
  }

  crosstets->restart();
  topnewtets->restart();
  if (botnewtets != NULL) {
    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 == NULL) {
          insph = insphere_s(p[1], p[0], p[2], p[3], p[4]);
          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 == NULL) {
              *pqueue = parybf;
              parybf->nextitem = NULL;
            } else {
              parybf->nextitem = *pqueue;
              *pqueue = parybf;
            }
          } // if (insph > 0)
        } // if (checksh.sh == NULL)
      }
    }
    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];
    } else {
      w[i] = 0;
    }
  }

  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 (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 == NULL) {
      *pqueue = parybf;
      parybf->nextitem = NULL;
    } else {
      // Search an item whose key is larger or equal to current key.
      prevbf = NULL;
      nextbf = *pqueue;
      //if (!b->flipinsert_random) { // Default use a priority queue.
        // Insert the item into priority queue.
        while (nextbf != NULL) {
          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 == NULL) {
        *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;
  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 = NULL;

  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 != NULL) {

    // Pop a face from the priority 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);
        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.
            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) {
            //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);
            }

            // Collect tets sharing at this edge.
            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 {
            // 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 != NULL)

  } // while (pqueue != NULL)

  if (bfacearray->objects > 0) {
    if (fcount == 0) {
      printf("!! No flip is found in %ld faces.\n", bfacearray->objects);
      terminatetetgen(this, 2); //assert(0);
    }
  }

  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);
  }
}

//============================================================================//
//                                                                            //
// 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, NULL, NULL, 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, NULL, NULL, NULL, NULL, NULL, NULL);
  carvecavity(crosstets, newtets, NULL);

  if ((splitsh != NULL) || (splitseg != NULL)) {
    // 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] != NULL) {
        subfacstack->newindex((void **) &parysh);
        *parysh = checksh;
      }
    }

    if (splitseg != NULL) {
      // 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 != NULL)

    // 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 != NULL) {
          if (neightet.tet[4] != NULL) {
            // Found an adjacent tet. It must be not in C(p).
            tsdissolve(neightet);
            fsymself(neightet);
            tsdissolve(neightet);
          }
        }
      }
      shellfacedealloc(subfaces, parysh->sh);
    }
    if (splitseg != NULL) {
      // Delete the old segment in sC(p).
      shellfacedealloc(subsegs, splitseg->sh);
    }

    // Clear working lists.
    caveshlist->restart();
    caveshbdlist->restart();
    cavesegshlist->restart();
  } // if ((splitsh != NULL) || (splitseg != NULL)) 

  // 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] != NULL) {
      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] != NULL) {
      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 not be 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;

  // Do not split a segment.
  for (i = 0; i < 3; i++) {
    sspivot(splitsh, splitseg);
    if (splitseg.sh == NULL) break;
    senextself(splitsh);
  }

  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;
  ivf.smlenflag = useinsertradius; // Return the closet mesh vertex.

  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, NULL, &ivf, cavpoints,
                       cavfaces, cavshells, newtets, crosstets, misfaces)) {
    if (ivf.iloc == (int) ENCSEGMENT) {
      pointdealloc(steinpt);
      // Split an encroached segment.
      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)) {
        terminatetetgen(this, 2); 
      }
      if (useinsertradius) {
        //save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen);
      }
      st_segref_count++;
      if (steinerleft > 0) steinerleft--;
    } else {
      terminatetetgen(this, 2); // assert(0);
    }
  } else {
    if (useinsertradius) {
      //save_facetpoint_insradius(steinpt, ivf.parentpt, ivf.smlen);
    }
    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 != NULL) continue;

    // Search the segment.
    dir = scoutsegment(sorg(splitseg), sdest(splitseg), &splitseg, &searchtet, 
                       &refpt, NULL);
    if (dir == SHAREEDGE) {
      // Found this segment, insert it.
      // 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 { 
      if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
        // Split the segment.
        makepoint(&steinpt, 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)) {
          terminatetetgen(this, 2);
        }
        if (useinsertradius) {
          //save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen);
        }
        st_segref_count++;
        if (steinerleft > 0) steinerleft--;
      } else {
        terminatetetgen(this, 2);
      }
    }
  } // 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] == NULL) 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);
          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 > 1) {
      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] == NULL) continue; // It is dead.
      if (isshtet(searchsh)) continue; // It is recovered.

      searchtet.tet = NULL;
      if (scoutsubface(&searchsh, &searchtet, 1)) continue;

      // The subface is missing. Form the missing region.
      //   Re-use 'tg_crosstets' for 'adjtets'.
      formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts);

      int searchflag = scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs);
      if (searchflag > 0) {
        // 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);
			// Put all subfaces in R back to tg_facfaces.
		    for (i = 0; i < tg_missingshs->objects; i++) {
		      parysh = (face *) fastlookup(tg_missingshs, i);
		      tg_facfaces->newindex((void **) &parysh1);
              *parysh1 = *parysh;
		    }
			success = 1;
            // 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);
              if (!scoutsubface(parysh, &searchtet, 1)) {
                // 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), paryseg,
                                 &searchtet, NULL, NULL);
              if (dir != SHAREEDGE) {
                terminatetetgen(this, 2);
              }
              // Insert this segment.
              // 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);
            }
            caveencseglist->restart();
          } // success - remesh cavity
        } // success - form cavity 
        else {
          terminatetetgen(this, 2); // Report a bug.
        } // Not success - form cavity
      } else {
        // Put all subfaces in R back to tg_facfaces.
		for (i = 0; i < tg_missingshs->objects; i++) {
		  parysh = (face *) fastlookup(tg_missingshs, i);
		  tg_facfaces->newindex((void **) &parysh1);
          *parysh1 = *parysh;
		}
        if (searchflag != -1) {
          // Some edge(s) in the missing regions were flipped.
          success = 1; 
        } else {
          restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, 
                        tg_missingshbds); // Only remove fake segments.
          // Choose an edge to split (set in recentsh)
          recentsh = searchsh;
		  success = 0; // Do refineregion();
        }
      } // if (scoutcrossedge)

      // 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);
      }
    } // 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;
  encseglist = NULL;
}

//============================================================================//
//                                                                            //
// 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");
  }

  makesegmentendpointsmap();
  makefacetverticesmap();

  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 =============================================================//
//                                                                            //
//                                                                            //

void tetgenmesh::sort_2pts(point p1, point p2, point ppt[2])
{
  if (pointmark(p1) < pointmark(p2)) {
    ppt[0] = p1;
    ppt[1] = p2;
  } else {
    ppt[0] = p2;
    ppt[1] = p1;
  }
}

void tetgenmesh::sort_3pts(point p1, point p2, point p3, point ppt[3])
{
  int i1 = pointmark(p1);
  int i2 = pointmark(p2);
  int i3 = pointmark(p3);

  if (i1 < i2) {
    if (i1 < i3) {
      ppt[0] = p1;
      if (i2 < i3) {
        ppt[1] = p2;
        ppt[2] = p3;
      } else {
        ppt[1] = p3;
        ppt[2] = p2;
      }
    } else {
      ppt[0] = p3;
      ppt[1] = p1;
      ppt[2] = p2;
    }
  } else { // i1 > i2
    if (i2 < i3) {
      ppt[0] = p2;
      if (i1 < i3) {
        ppt[1] = p1;
        ppt[2] = p3;
      } else {
        ppt[1] = p3;
        ppt[2] = p1;
      }
    } else {
      ppt[0] = p3;
      ppt[1] = p2;
      ppt[2] = p1;
    }
  }
}


//============================================================================//
//                                                                            //
// is_collinear_at()    Check if three vertices (from left to right): left,   //
//                      mid, and right are collinear.                         //
//                                                                            //
//============================================================================//

bool tetgenmesh::is_collinear_at(point mid, point left, point right)
{
  REAL v1[3], v2[3];
  
  v1[0] =  left[0] - mid[0];
  v1[1] =  left[1] - mid[1];
  v1[2] =  left[2] - mid[2];
  
  v2[0] = right[0] - mid[0];
  v2[1] = right[1] - mid[1];
  v2[2] = right[2] - mid[2];
  
  REAL L1 = sqrt(v1[0]*v1[0]+v1[1]*v1[1]+v1[2]*v1[2]);
  REAL L2 = sqrt(v2[0]*v2[0]+v2[1]*v2[1]+v2[2]*v2[2]);
  REAL D = (v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]);

  REAL cos_ang = D / (L1 * L2);
  return cos_ang < cos_collinear_ang_tol;
}

//============================================================================//
//                                                                            //
// is_segment()    Check if the two vertices are endpoints of a segment.      //
//                                                                            //
//============================================================================//

bool tetgenmesh::is_segment(point p1, point p2)
{
  if (pointtype(p1) == RIDGEVERTEX) {
    if (pointtype(p2) == RIDGEVERTEX) {
      // Check if p2 is connect to p1.
      int idx = pointmark(p1);
      for (int i = idx_segment_ridge_vertex_list[idx];
             i < idx_segment_ridge_vertex_list[idx+1]; i++) {
        if (segment_ridge_vertex_list[i] == p2) {
          return true;
        }
      }
    } else if (pointtype(p2) == FREESEGVERTEX) {
      // Check if the segment contains p2 has one if its endpoints be p1.
      face parsentseg;
      sdecode(point2sh(p2), parsentseg);
      int segidx = getfacetindex(parsentseg);
      if ((segmentendpointslist[segidx*2] == p1) ||
          (segmentendpointslist[segidx*2+1] == p1)) {
        return true;
      }
    }
  } else {
    if (pointtype(p1) == FREESEGVERTEX) {
      if (pointtype(p2) == RIDGEVERTEX) {
        face parsentseg;
        sdecode(point2sh(p1), parsentseg);
        int segidx = getfacetindex(parsentseg);
        if ((segmentendpointslist[segidx*2] == p2) ||
            (segmentendpointslist[segidx*2+1] == p2)) {
          return true;
        }
      } else if (pointtype(p2) == FREESEGVERTEX) {
        face parsentseg1, parsentseg2;
        sdecode(point2sh(p1), parsentseg1);
        sdecode(point2sh(p2), parsentseg2);
        int segidx1 = getfacetindex(parsentseg1);
        int segidx2 = getfacetindex(parsentseg2);
        if (segidx1 == segidx2) {
          return true;
        }
      }
    }
  }
  
  return false;
}

//============================================================================//
//                                                                            //
// valid_constrained_f23()    Validate a 2-3 flip.                            //
//                                                                            //
// The purpose of the following check is to avoid creating a degenrated face  //
//   (and subface) whose three vertices are nearly on one segment or on two   //
//   nearly collinear segments.                                               //
//                                                                            //
// "checktet" is a face (a,b,c) which is 2-3 flippable, and (d,e) will be     //
//   the new edge after this flip.                                            //
//                                                                            //
// return true if this 2-3 flip is good, otherwise, return false.             //
//                                                                            //
//============================================================================//

bool tetgenmesh::valid_constrained_f23(triface& checktet, point pd, point pe)
{
  bool validflag = true;

  triface spintet;
  face checkseg1, checkseg2;
  point checkpt;

  for (int k = 0; k < 3; k++) {
    checkpt = org(checktet);
    esym(checktet, spintet);
    enextself(spintet); // [x, d], x = a,b,c
    tsspivot1(spintet, checkseg1);
    bool isseg = (checkseg1.sh != NULL);
    if (!isseg && boundary_recovery_flag) {
      isseg = is_segment(checkpt, pd);
    }
    if (isseg) {
      fsym(checktet, spintet);
      esymself(spintet);
      eprevself(spintet);
      tsspivot1(spintet, checkseg2);
      isseg = (checkseg2.sh != NULL);
      if (!isseg && boundary_recovery_flag) {
        isseg = is_segment(checkpt, pe);
      }
      if (isseg) {
        if (pointtype(checkpt) == FREESEGVERTEX) {
          // In this case, the two subsegments (checkseg1, checkseg2)
          //   must belong to the same segment, do not flip.
          validflag = false;
          break;
        } else {
          // Check if three vertices are nearly collinear. The middle
          //   vertex is checkpt.
          if ((checkpt != dummypoint) &&
              (pe != dummypoint) &&
              (pd != dummypoint)) {
            if (is_collinear_at(checkpt, pe, pd)) {
              validflag = false;
              break;
            }
          }
        }
      } // if (isseg)
    } // if (isseg)
    enextself(checktet);
  } // k

  return validflag;
}

//============================================================================//
//                                                                            //
// valid_constrained_f32()    Validate a 3-2 flip.                            //
//                                                                            //
// Avoid creating a degenerated tetrahedral face whose three vertices are on  //
//   one (sub)segment. abtets[0], abdtets[1], abtets[2] are three tets        //
//   at the flipping edge (a,b), the new face will be (c, d, e).              //
//  The only new face we will create is (c,d,e), make sure that it is not     //
//  a (nearly) degenerated face. If the vertex c is RIDGEVEETEX or            //
//  FREESEGVERTEX, then the edges (c, d) and (c, e) should not on one segment.//
//  The same for the vertex d and e.                                          //
//                                                                            //
// return true if this 3-2 flip is good, otherwise, return false.             //
//                                                                            //
//============================================================================//

bool tetgenmesh::valid_constrained_f32(triface* abtets, point pa, point pb)
{
  bool validflag = true; // default.

  triface spintet;
  face checksegs[3]; // edges: [c,d], [d,e], and [e,c]
  point chkpt, leftpt, rightpt;

  // Check edges [c,d], [d,e], and [e,c]
  for (int k = 0; k < 3; k++) { // [a,b,c], [a,b,d], [a,b,e]
    enext(abtets[k], spintet);
    esymself(spintet);
    eprevself(spintet); // [c,d], [d,e], and [e,c]
    tsspivot1(spintet, checksegs[k]);
    // Ignore a temporaray segment (used in recoversubfaces()).
    if (checksegs[k].sh != NULL) {
      if (smarktest2ed(checksegs[k])) {
        checksegs[k].sh = NULL;
      }
    }
  } // k

  for (int k = 0; k < 3; k++) {
    chkpt   = apex(abtets[k]);         // pc
    leftpt  = apex(abtets[(k+2)%3]);   // pe
    rightpt = apex(abtets[(k+1)%3]);   // pd
    bool isseg = (checksegs[k].sh != NULL); // [c,d]
    if (!isseg && boundary_recovery_flag) {
      isseg = is_segment(chkpt, rightpt);
    }
    if (isseg) {
      isseg = (checksegs[(k+2)%3].sh != NULL); // [e,c]
      if (!isseg && boundary_recovery_flag) {
        isseg = is_segment(chkpt, leftpt);
      }
      if (isseg) {
        if (pointtype(chkpt) == FREESEGVERTEX) {
          validflag = false;
          break;
        } else {
          if ((chkpt != dummypoint) &&
              (leftpt != dummypoint) &&
              (rightpt != dummypoint)) {
            if (is_collinear_at(chkpt, leftpt, rightpt)) {
              validflag = false;
              break;
            }
          }
        }
      }
    }
  } // k

  return validflag;
}

//============================================================================//
//                                                                            //
// 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] != NULL) {
    // 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], 
                                  NULL, 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 ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || 
                  (dir == TOUCHFACE)) {
                // should be a self-intersection.
                rejflag = 1;
              }
            } // dir
          } 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;
              }
            }
            else if (dir == ACROSSFACE) {
              //assert(0); // This should be not possible.
              terminatetetgen(this, 2);
            } 
            else {
              if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || 
                  (dir == TOUCHFACE)) {
                // This should be caused by a self-intersection.
                rejflag = 1; // Do not flip.
              }
            }
          }
        } // 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], NULL, 
                                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] != NULL)

  if ((fc->fac[0] != NULL) && !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, 
                              NULL, 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] != NULL)

  if ((fc->remvert != NULL) && !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.
    badface bf; // used by get_tetqual(...)
    REAL cosmaxd = 0, diff;
    if (fliptype == 1) {
      // We assume that neither 'a' nor 'b' is dummypoint.
      // 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)) {
        REAL min_cosmaxd = 1.0, max_asp = 0; // record the worst quality.
        // Get the largest dihedral angle of [e,d,b,c].
        if (get_tetqual(pe, pd, pb, pc, &bf)) {
          cosmaxd = bf.cent[0];
          diff = cosmaxd - fc->cosdihed_in;
          if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
        } else {
          diff = 0.0; // no improve.
        }
        if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
          rejflag = 1;
        } else {
          // Record the largest new angle.
          min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd);
          max_asp = (max_asp > bf.key ? max_asp : bf.key);
          // Get the largest dihedral angle of [e,d,c,a].
          if (get_tetqual(pe, pd, pc, pa, &bf)) {
            cosmaxd = bf.cent[0];
            diff = cosmaxd - fc->cosdihed_in;
            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
          } else {
            diff = 0.0; // no improve.
          }
          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
            rejflag = 1;
          } else {
            // Record the largest new angle.
            min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd);
            max_asp = (max_asp > bf.key ? max_asp : bf.key);
            // save the worst quality.
            fc->cosdihed_out = (fc->cosdihed_out < min_cosmaxd ? fc->cosdihed_out : min_cosmaxd);
            fc->max_asp_out = (fc->max_asp_out > max_asp ? fc->max_asp_out : max_asp);
          }
        }
      } // 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.
      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)) {
          REAL min_cosmaxd = 1.0, max_asp = 0; // record the worst quality.
          // Get the largest dihedral angle of [a,b,c,d].
          if (get_tetqual(pa, pb, pc, pd, &bf)) {
            cosmaxd = bf.cent[0];
            diff = cosmaxd - fc->cosdihed_in;
            if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
          } else {
            diff = 0.0; // no improve.
          }
          if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
            rejflag = 1;
          } else {
            // Record the largest new angle.
            min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd);
            max_asp = (max_asp > bf.key ? max_asp : bf.key);
            // Get the largest dihedral angle of [b,a,c,e].
            if (get_tetqual(pb, pa, pc, pe, &bf)) {
              cosmaxd = bf.cent[0];
              diff = cosmaxd - fc->cosdihed_in;
              if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
            } else {
              diff = 0.0; // no improve.
            }
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd);
              max_asp = (max_asp > bf.key ? max_asp : bf.key);
              // save the worst quality.
              fc->cosdihed_out = (fc->cosdihed_out < min_cosmaxd ? fc->cosdihed_out : min_cosmaxd);
              fc->max_asp_out = (fc->max_asp_out > max_asp ? fc->max_asp_out : max_asp);
            }
          }
        }
      } else { // level > 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].
            if (get_tetqual(pb, pa, pc, pe, &bf)) {
              cosmaxd = bf.cent[0];
              diff = cosmaxd - fc->cosdihed_in;
              if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
            } else {
              diff = 0.0; // no improve.
            }
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              // save the worst quality.
              fc->cosdihed_out = (fc->cosdihed_out < cosmaxd ? fc->cosdihed_out : cosmaxd);
              fc->max_asp_out = (fc->max_asp_out > bf.key ? fc->max_asp_out : bf.key);
            }
          }
        } else {
          // 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].
            if (get_tetqual(pa, pb, pc, pd, &bf)) {
              cosmaxd = bf.cent[0];
              diff = cosmaxd - fc->cosdihed_in;
              if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding.
            } else {
              diff = 0.0; // no improve.
            }
            if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
              rejflag = 1;
            } else {
              // Record the largest new angle.
              // save the worst quality.
              fc->cosdihed_out = (fc->cosdihed_out < cosmaxd ? fc->cosdihed_out : cosmaxd);
              fc->max_asp_out = (fc->max_asp_out > bf.key ? fc->max_asp_out : bf.key);
            }
          }
        } // edgepivot
      } // level
    }
  } // if (fc->remove_large_angle && !rejflag)

  return rejflag;
}

//============================================================================//
//                                                                            //
// removeedgebyflips()    Attempt to 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, otherwise, 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].
  int subface_count = 0; // count the # of subfaces at this edge.
  n = 0;
  spintet = *flipedge;
  while (1) {
    if (issubface(spintet)) subface_count++;
    n++;
    fnextself(spintet);
    if (spintet.tet == flipedge->tet) break;
  }
  if (n < 3) {
    // It is only possible when the mesh contains inverted tetrahedra.  
    terminatetetgen(this, 2); // Report a bug
  }

  if (fc->noflip_in_surface) {
    if (subface_count > 0) {
      return 0;
    }
  }

  //if ((b->flipstarsize > 0) && (n > (b->flipstarsize+4))) {
  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;
  for (i = 0; i < n; i++) {
    abtets[i] = spintet;
    setelemcounter(abtets[i], 1);
    fnextself(spintet);
  }


  // 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.                      //
//                                                                            //
// ASSUMPTION: 'flipface' must not be a subface or a hull face.               //
//                                                                            //
//============================================================================//

int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc)
{
  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) {
    triface checkface = fliptets[0];
    if (!valid_constrained_f23(checkface, pd, pe)) {
      return 0; //reducflag = 0;
    }
  }

  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) {
      if (b->verbose > 3) {
        printf("      Face is removed by removing an edge.\n");
      }
      return 1;
    }
  }

  // Face is not removed.
  return 0;
}

//============================================================================//
//                                                                            //
// recoveredgebyflips()    Recover an edge in current tetrahedralization.     //
//                                                                            //
// If the edge is recovered, 'searchtet' returns a tet containing the edge.   //
//                                                                            //
// 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.                                    //
//                                                                            //
// The parameter 'sedge' is used to report self-intersection. If it is not    //
// a NULL, it is EITHER a segment OR a subface that contains this edge.       //
//                                                                            //
// This routine assumes that the tetrahedralization is convex.                //
//                                                                            //
//============================================================================//

int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face *sedge,
                                   triface* searchtet, int fullsearch, int& idir)
{
  flipconstraints fc;
  enum interresult dir;

  idir = (int) DISJOINT; // init.

  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 {
        if (sedge != NULL) {
          // It is a segment or a subedge (an edge of a facet).
          // Check and report if there exists a self-intersection.
          insertvertexflags ivf;
          bool intersect_flag = false;
          point nearpt = dest(*searchtet);
          ivf.iloc = ONVERTEX;
          
          if (sedge->sh[5] == NULL) {
            // It is a segment.
            if (!issteinerpoint(nearpt)) {
              // It is an input point.
              if (!b->quiet && !b->nowarning) {
                int segidx = getfacetindex(*sedge);
                point p1 = segmentendpointslist[segidx*2];
                point p2 = segmentendpointslist[segidx*2+1];
                point tmppt = NULL;
                if (is_segment(p1, nearpt)) tmppt = p1;
                else if (is_segment(p2, nearpt)) tmppt = p2;
                if (tmppt != NULL) {
                  printf("Warning:  Two line segments are %s overlapping.\n",
                         ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                  printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                  printf("  2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt));
                } else {
                  printf("Warning:  A vertex lies %s on a line segment.\n",
                         ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                  printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                  printf("  2nd: [%d].\n", pointmark(nearpt));
                }
              }
              intersect_flag = true;
            } else {
              if (pointtype(nearpt) == FREESEGVERTEX) {
                // Check if two segments are (nearly) intersecting.
                int segidx = getfacetindex(*sedge);
                face parsentseg;
                sdecode(point2sh(nearpt), parsentseg);
                int segidx2 = getfacetindex(parsentseg);
                if (segidx2 != segidx) {
                  if (!b->quiet && !b->nowarning) { // -no -Q no -W
                    point p1 = segmentendpointslist[segidx*2];
                    point p2 = segmentendpointslist[segidx*2+1];
                    point p3 = segmentendpointslist[segidx2*2];
                    point p4 = segmentendpointslist[segidx2*2+1];
                    printf("Warning:  Two line segments are %s crossing.\n",
                           ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                    printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                    printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
                  }
                  intersect_flag = true;
                } else {
                  //if (ivf.iloc == ONVERTEX) {
                  terminatetetgen(this, 2); // This should not be possible.
                  //}
                }
              } else if (pointtype(nearpt) == FREEFACETVERTEX) {
                // This case is very unlikely.
                terminatetetgen(this, 2); // to debug...
                if (!b->quiet && !b->nowarning) { // -no -Q no -W
                  //face parsentsh;
                  //sdecode(point2sh(nearpt), parsentsh);
                  printf("Warning:  A segment and a facet intersect.\n");
                }
                intersect_flag = true;
              } else {
                // other cases...
                terminatetetgen(this, 2); // to be checked.
              }
            }
          } else {
            // It is an edge of a facet.
            if (!issteinerpoint(nearpt)) {
              if (!b->quiet && !b->nowarning) { // no "-Q -W"
                point p1 =  sorg(*sedge);
                point p2 = sdest(*sedge);
                point p3 = sapex(*sedge);
                printf("Warning:  A vertex lies on a facet.\n");
                printf("  vertex: [%d]\n", pointmark(nearpt));
                printf("  facet triangle: [%d,%d,%d], tag(%d).\n",
                       pointmark(p1), pointmark(p2), pointmark(p3),
                       shellmark(*sedge));
              }
              intersect_flag = true;
            } else {
              // A Steiner point.
              if (pointtype(nearpt) == FREESEGVERTEX) {
                // A facet and a segment is intersecting.
                if (!b->quiet && !b->nowarning) {
                  printf("Warning:  A facet and a segment intersect.\n");
                  printf("  ...\n");
                }
                intersect_flag = true;
              } else if (pointtype(nearpt) == FREEFACETVERTEX) {
                // Check if two facets are intersecting.
                if (!b->quiet && !b->nowarning) {
                  printf("Warning:  Two facets intersect.\n");
                  printf("  ...\n");
                }
                intersect_flag = true;
              } else {
                // A FREEVOLVERTEX.
                // This is not a real self-intersection.
                terminatetetgen(this, 2); // check this case.
              }
            }
          }
          
          if (intersect_flag) {
            idir = (int) SELF_INTERSECT;
          }
        } // if (sedge != NULL)
        return 0;
      }
    } // if (dir == ACROSSVERT)

    // The edge is missing. 

    // Try to remove the first intersecting face/edge.
    enextesymself(*searchtet); // Go to the opposite face.

    if (dir == ACROSSFACE) {
      if (checksubfaceflag) {
        if (issubface(*searchtet)) {
          if (sedge) {
            // A self-intersection is detected.
            if (!b->quiet && !b->nowarning) {
              bool is_seg = (sedge->sh[5] == NULL);
              if (is_seg) {
                face fac; tspivot(*searchtet, fac);
                int segidx = getfacetindex(*sedge);
                point p1 = segmentendpointslist[segidx*2];
                point p2 = segmentendpointslist[segidx*2+1];
                printf("Warning:  A segment and a facet exactly intersect.\n");
                printf("  seg  : [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  facet triangle: [%d,%d,%d] tag(%d).\n",
                       pointmark(sorg(fac)), pointmark(sdest(fac)),
                       pointmark(sapex(fac)), shellmark(fac));
              } else {
                // It is a subedge of a facet.
                point *ppt = (point *) &(sedge->sh[3]);
                printf("Warning:  Two facets exactly intersect.\n");
                printf("  1st facet triangle: [%d,%d,%d] tag(%d).\n",
                       pointmark(ppt[0]), pointmark(ppt[1]),
                       pointmark(ppt[2]), shellmark(*sedge));
                face fac; tspivot(*searchtet, fac);
                ppt = (point *) &(fac.sh[3]);
                printf("  2nd facet triangle: [%d,%d,%d] tag(%d).\n",
                       pointmark(ppt[0]), pointmark(ppt[1]),
                       pointmark(ppt[2]), shellmark(fac));
              }
            }
            idir = (int) SELF_INTERSECT;
          }
          return 0;
        } // if (issubface(*searchtet))
      }
      // Try to flip a crossing face.
      if (removefacebyflips(searchtet, &fc)) {
        continue;
      }
    } else if (dir == ACROSSEDGE) {
      if (checksubsegflag) {
        if (issubseg(*searchtet)) {
          if (sedge) {
            // A self-intersection is detected.
            if (!b->quiet && !b->nowarning) { // no -Q, -W
              bool is_seg = (sedge->sh[5] == NULL);
              if (is_seg) {
                face seg; tsspivot1(*searchtet, seg);
                int segidx  = getfacetindex(*sedge);
                int segidx2 = getfacetindex(seg);
                if (segidx != segidx2) {
                  point p1 = segmentendpointslist[segidx*2];
                  point p2 = segmentendpointslist[segidx*2+1];
                  point p3 = segmentendpointslist[segidx2*2];
                  point p4 = segmentendpointslist[segidx2*2+1];
                  printf("Warning:  Two segments exactly intersect.\n");
                  printf("  1st seg  [%d,%d] tag(%d).\n",
                         pointmark(p1), pointmark(p2), shellmark(*sedge));
                  printf("  2nd seg: [%d,%d] tag(%d).\n",
                         pointmark(p3), pointmark(p4), shellmark(seg));
                } else {
                  terminatetetgen(this, 2);
                }
              } else {
                // It is a subedge of a facet.
                point *ppt = (point *) &(sedge->sh[3]);
                printf("Warning:  A facet and a segment exactly intersect.\n");
                printf("  facet triangle: [%d,%d,%d] tag(%d).\n",
                       pointmark(ppt[0]), pointmark(ppt[1]),
                       pointmark(ppt[2]), shellmark(*sedge));
                face seg; tsspivot1(*searchtet, seg);
                ppt = (point *) &(seg.sh[3]);
                printf("  seg: [%d,%d] tag(%d).\n",
                       pointmark(ppt[0]), pointmark(ppt[1]), shellmark(seg));
              }
            }
            idir = (int) SELF_INTERSECT;
          }
          return 0;
        }
      }
      // Try to flip an intersecting edge.
      if (removeedgebyflips(searchtet, &fc) == 2) {
        continue;
      }
    } else {
      terminatetetgen(this, 2); // report a bug
    }

    // 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);

      // 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.
          if (dir == DISJOINT) {
            terminatetetgen(this, 2);
          }
        } else if (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
        } else {
          terminatetetgen(this, 2); // Report a bug
        }

        // 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 {
            return 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 (checksubfaceflag) {
            if (issubface(*searchtet)) {
              return 0;
            }
          }
          if (removefacebyflips(searchtet, &fc)) {
            success = 1;
            break; // Loop I-I 
          }
        } else if (dir == ACROSSEDGE) {
          if (checksubsegflag) {
            if (issubseg(*searchtet)) {
              return 0;
            }
          }
          if (removeedgebyflips(searchtet, &fc) == 2) {
            success = 1;
            break; // Loop I-I
          }
        } else if (dir == ACROSSVERT) {
          return 0;
        } else {
          terminatetetgen(this, 2); 
        }

        // The face/edge is not flipped.
        if ((searchtet->tet == NULL) ||
            (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) {
            if (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 = NULL;
                  break; // Not find.
                }
	          } // while (1)
              if (searchtet->tet != NULL) {
                if (oppo(*searchtet) != bakface.foppo) {
                  fsymself(*searchtet);
                  if (oppo(*searchtet) != bakface.foppo) {
                    // The original (intersecting) tet has been flipped.
                    searchtet->tet = NULL;
                    break; // Not find.
                  }
                }
              }
            } else {
              searchtet->tet = NULL; // Not find.
            }
          } else {
            searchtet->tet = NULL; // Not find.
          }
          if (searchtet->tet == NULL) {
            success = 0; // This face/edge has been destroyed.
            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 splitsliverflag, 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;


  if (splitsliverflag) {
    // randomly pick a tet.
    int idx = rand() % n;

    // Calulcate the barycenter of this tet.
    point pa = org(abtets[idx]);
    point pb = dest(abtets[idx]);
    pc = apex(abtets[idx]);
    pd = oppo(abtets[idx]);

    makepoint(&steinerpt, FREEVOLVERTEX);
    for (i = 0; i < 3; i++) {
      steinerpt[i] = (pa[i] + pb[i] + pc[i] + pd[i]) / 4.;
    }


    worktet = abtets[idx];
    ivf.iloc = (int) OUTSIDE; // need point location.
    ivf.bowywat = 1;
    //ivf.lawson = 0;
    ivf.lawson = 2; // Do flips to recover Delaunayness.
    ivf.rejflag = 0;
    ivf.chkencflag = chkencflag;
    ivf.sloc = 0;
    ivf.sbowywat = 0;
    ivf.splitbdflag = 0;
    ivf.validflag = 1;
    ivf.respectbdflag = 1;
    ivf.assignmeshsize = b->metric;

    if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) {
      // The vertex has been inserted.
      if (flipstack != NULL) {
        recoverdelaunay();
      }
      st_volref_count++;
      if (steinerleft > 0) steinerleft--;
      return 1;
    } else {
      // Not inserted.
      pointdealloc(steinerpt);
      return 0;
    }
  } // if (splitsliverflag)

  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) {
    // Insert this Steiner point.

    // 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]));
      worktet = abtets[0];
    }

    // Insert the new point into the tetrahedralization T.
    if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) {
      // The vertex has been inserted.
      st_volref_count++;
      if (steinerleft > 0) steinerleft--;
      return 1;
    } else {
      // Not inserted.
      pointdealloc(steinerpt);
      return 0;
    }
  }

  //if (!success) {
    return 0;
  //}
}

//============================================================================//
//                                                                            //
// add_steinerpt_in_segment()    Add a Steiner point inside a segment.        //
//                                                                            //
//============================================================================//

int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel, int& idir)
{
  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);

  idir = DISJOINT; // init.
  
  // sort the vertices
  //if (pointmark(startpt) > pointmark(endpt)) {
  //  endpt = sorg(*misseg);
  //  startpt = sdest(*misseg);
  //}
  

  fc.seg[0] = startpt;
  fc.seg[1] = endpt;
  fc.checkflipeligibility = 1;
  fc.collectencsegflag = 1;

  point2tetorg(startpt, searchtet);
  dir = finddirection(&searchtet, endpt);
  if (dir == ACROSSVERT) {
    return 0;
  }

  // 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);
  } else if (dir == ACROSSEDGE) {
    // An edge is intersected with the segment. Try to flip it.
    success = removeedgebyflips(&searchtet, &fc);
  }

  split = 0;
  for (i = 0; i < caveencseglist->objects; i++) {
    paryseg = (face *) fastlookup(caveencseglist, i);
    suninfect(*paryseg);
    // Calculate the shortest edge between the two lines.
    pc = sorg(*paryseg);
    pd = sdest(*paryseg);
    
    // sort the vertices
    //if (pointmark(pc) > pointmark(pd)) {
    //  pd = sorg(*paryseg);
    //  pc = 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]);
    }
  }

  // Check if the two segments are nearly crossing each other.
  pc = sorg(candseg);
  pd = sdest(candseg);
  if (is_collinear_at(steinerpt, pc, pd)) { // -p///#, default 179.9 degree
    if (!b->quiet && !b->nowarning) { // no -Q, -W
      int segidx = getfacetindex(*misseg);
      point p1 = segmentendpointslist[segidx*2];
      point p2 = segmentendpointslist[segidx*2+1];
      int segidx2 = getfacetindex(candseg);
      point p3 = segmentendpointslist[segidx2*2];
      point p4 = segmentendpointslist[segidx2*2+1];
      printf("Warning:  Two line segments are almost crossing.\n");
      printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
      printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
    }

    // calculate a new angle tolerance.
    REAL collinear_ang = interiorangle(steinerpt, pc, pd, NULL) / PI * 180.;
    double ang_diff = collinear_ang - b->collinear_ang_tol;
    double new_ang_tol = collinear_ang + ang_diff / 180.;
 
    if (new_ang_tol < 180.0) { // no -Q, -W
      // Reduce the angle tolerance to detect collinear event.
      if (!b->quiet && !b->nowarning) {
        printf("  Reducing collinear tolerance from %g to %g degree.\n",
               b->collinear_ang_tol, new_ang_tol);
      }
      b->collinear_ang_tol = new_ang_tol;
      cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180.0 * PI);
    } else {
      // Report a self-intersection event due to epsilon.
      if (!b->quiet && !b->nowarning) { // no -Q, -W
        printf("  Cannot reduce the current collinear tolerance (=%g degree).\n",
               b->collinear_ang_tol);
      }
      idir = SELF_INTERSECT;
      pointdealloc(steinerpt);
      return 0;
    }
  }

  // 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);
    // for create_a_shorter_edge().
    setpoint2sh(steinerpt, sencode(*misseg));
  } else {
    splitsh.sh = NULL;
    splitseg.sh = NULL;
  }
  ivf.iloc = (int) OUTSIDE;
  ivf.bowywat = 1;
  //ivf.lawson = 0;
  ivf.lawson = 2; // Do flips to recover Delaunayness.
  ivf.rejflag = 0;
  ivf.chkencflag = 0;
  ivf.sloc = (int) ONEDGE;
  ivf.sbowywat = 1; // split surface mesh separately, new subsegments are
                    //   pushed into "subsegstack".
  ivf.splitbdflag = 0;
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric; 

  if (insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) {
    if (flipstack != NULL) {
      recoverdelaunay();
    }
  } else {
    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 recovering a seg.   //
//                                                                            //
// Tries to add a Steiner point in the volume (near this segment) which will  //
// help to recover this segment.  This segment itself is not split.           //
//                                                                            //
// 'splitsliverflag' is a parameter used in the subroutine add_steiner_in_    //
// schonhardpoly().                                                           //
//                                                                            //
//============================================================================//

int tetgenmesh::add_steinerpt_to_recover_edge(point startpt, point endpt,
  face* misseg, int splitsegflag, int splitsliverflag, int& idir)
{
  triface *abtets, searchtet, spintet;
  face splitsh;
  face *paryseg;
  point pa, pb, pd, steinerpt, *parypt;
  enum interresult dir;
  insertvertexflags ivf;
  int types[2], poss[4];
  int n, endi, success;
  int t1ver;
  int i;

  idir = (int) DISJOINT;

  if (misseg != NULL) {
    startpt = sorg(*misseg);
    if (pointtype(startpt) == FREESEGVERTEX) {
      sesymself(*misseg);
      startpt = sorg(*misseg);
    }
    endpt = sdest(*misseg);
  }

  
  point2tetorg(startpt, searchtet);
  dir = finddirection(&searchtet, endpt);


  if (dir == ACROSSVERT) {
    if (dest(searchtet) == endpt) {
      // This edge exists.
      if ((misseg != NULL) && (subsegstack != NULL)) {
        // Add the missing segment back to the recovering list.
        subsegstack->newindex((void **) &paryseg);
        *paryseg = *misseg;
      }
      return 1;
    } else {
      // This edge crosses a vertex (not endpt).
      bool intersect_flag = false; // return
      if (misseg != NULL) {
        // Check whether there exists a self-intersection.
        point nearpt = dest(searchtet);
        ivf.iloc = ONVERTEX;
        // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX);
        int segidx = getfacetindex(*misseg);
        point p1 = segmentendpointslist[segidx*2];
        point p2 = segmentendpointslist[segidx*2+1];

        if (!issteinerpoint(nearpt)) {
          // It is an input point.
          if (!b->quiet && !b->nowarning) {
            point tmppt = NULL;
            if (is_segment(p1, nearpt)) tmppt = p1;
            else if (is_segment(p2, nearpt)) tmppt = p2;
            if (tmppt != NULL) {
              printf("Warning:  Two line segments are %s overlapping.\n",
                     ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
              printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
              printf("  2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt));
            } else {
              printf("Warning:  A vertex lies %s on a line segment.\n",
                     ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
              printf("  segment: [%d,%d].\n", pointmark(p1), pointmark(p2));
              printf("  vertex : [%d].\n", pointmark(nearpt));
            }
          }
          intersect_flag = true;
        } else {
          if (pointtype(nearpt) == FREESEGVERTEX) {
            // Check if two segments are exactly intersecting.
            face parsentseg;
            sdecode(point2sh(nearpt), parsentseg);
            int segidx2 = getfacetindex(parsentseg);
            if (segidx2 != segidx) {
              if (!b->quiet && !b->nowarning) {
                point p3 = segmentendpointslist[segidx2*2];
                point p4 = segmentendpointslist[segidx2*2+1];
                printf("Warning:  Two line segments are %s crossing.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
              }
              intersect_flag = true;
            } else {
              if (ivf.iloc == ONVERTEX) {
                terminatetetgen(this, 2); // This should not be possible.
              }
            }
          } else {
            // other cases...
            terminatetetgen(this, 2);
          }
        }
      } // if (misseg != NULL)
      if (intersect_flag) {
        idir = (int) SELF_INTERSECT;
      }
      return 0;
    }
  } // if (dir == ACROSSVERT) {

  enextself(searchtet);

  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);

    if (pd == endpt) {
      if (misseg != NULL) {
        // Calclate the smallest angle between (a,b,c) and (startpt, endpt).
        triface tmptet;
        REAL ang, collinear_ang = 0.;
        for (int k = 0; k < 3; k++) {
          ang = interiorangle(org(searchtet), startpt, endpt, NULL); // in [0, PI]
          if (ang > collinear_ang) {
            collinear_ang = ang;
            tmptet = searchtet; // org(tmptet)
          }
          enextself(searchtet);
        }
        collinear_ang = collinear_ang / PI * 180.; // in degree

        if (collinear_ang > b->collinear_ang_tol) { // -p///#, default 179.9 degree
          // Report a self-intersection event due to epsilon.
          if (!b->quiet && !b->nowarning) { // no -Q, -W
            point nearpt = org(tmptet);
            ivf.iloc = NEARVERTEX;
            // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX);
            int segidx = getfacetindex(*misseg);
            point p1 = segmentendpointslist[segidx*2];
            point p2 = segmentendpointslist[segidx*2+1];
              
            if (!issteinerpoint(nearpt)) {
              point tmppt = NULL;
              if (is_segment(p1, nearpt)) tmppt = p1;
              else if (is_segment(p2, nearpt)) tmppt = p2;
              if (tmppt != NULL) {
                printf("Warning:  Two line segments are %s overlapping.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt));
              } else {
                printf("Warning:  A vertex lies %s on a line segment.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  segment: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  vertex : [%d].\n", pointmark(nearpt));
              }
            } else {
              if (pointtype(nearpt) == FREESEGVERTEX) {
                // Check if two segments are nearly intersecting.
                face parsentseg;
                sdecode(point2sh(nearpt), parsentseg);
                int segidx2 = getfacetindex(parsentseg);
                if (segidx2 != segidx) {
                  //if (!b->quiet && !b->nowarning) {
                  point p3 = segmentendpointslist[segidx2*2];
                  point p4 = segmentendpointslist[segidx2*2+1];
                  printf("Warning:  Two line segments are %s crossing.\n",
                         ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                  printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                  printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
                  //}
                  //intersect_flag = true;
                } else {
                  //if (ivf.iloc == ONVERTEX) {
                  terminatetetgen(this, 2); // This should not be possible.
                  //}
                }
              } else {
                // Other case to report.
                //  assert(0); // to do...
                terminatetetgen(this, 2);
              }
            }
          }
          
          // calculate a new angle tolerance.
          double ang_diff = collinear_ang - b->collinear_ang_tol;
          double new_ang_tol = collinear_ang + ang_diff / 180.;
          
          if (new_ang_tol < 180.) {
            // Reduce the angle tolerance to detect collinear event.
            if (!b->quiet && !b->nowarning) {
              printf("  Reducing collinear tolerance from %g to %g degree.\n",
                     b->collinear_ang_tol, new_ang_tol);
            }
            b->collinear_ang_tol = new_ang_tol;
            cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180. * PI);
          
            // This segment can be recovered by a 2-3 flip.
            if (subsegstack != NULL) {
              // Add the missing segment back to the recovering list.
              subsegstack->newindex((void **) &paryseg);
              *paryseg = *misseg;
            }
            return 1;
          } else {
            if (!b->quiet && !b->nowarning) {
              printf("  Cannot reduce the current collinear tolerance (=%g degree).\n",
                     b->collinear_ang_tol);
            }
            idir = (int) SELF_INTERSECT;
            return 0;
          }
        } else {
          // This segment can be recovered by a 2-3 flip.
          if (subsegstack != NULL) {
            // Add the missing segment back to the recovering list.
            subsegstack->newindex((void **) &paryseg);
            *paryseg = *misseg;
          }
          return 1;
        }
      } else {
        // This edge (not a segment) can be recovered by a 2-3 flip.
        return 1;
      }
    } // if (pd == endpt)

    if (issubface(searchtet)) {
      if (misseg != NULL) {
        terminatetetgen(this, 2);
        // Report a segment and a facet intersect.
        if (!b->quiet && !b->nowarning) {
          face fac; tspivot(searchtet, fac);
          int segidx = getfacetindex(*misseg);
          point p1 = segmentendpointslist[segidx*2];
          point p2 = segmentendpointslist[segidx*2+1];
          printf("Warning:  A segment and a facet exactly intersect.\n");
          printf("  segment  : [%d,%d].\n", pointmark(p1), pointmark(p2));
          printf("  facet triangle: [%d,%d,%d] tag(%d).\n",
                 pointmark(org(searchtet)), pointmark(dest(searchtet)),
                 pointmark(apex(searchtet)), shellmark(fac));
        }
        idir = (int) SELF_INTERSECT;
      }
      return 0;
    } // if (issubface(searchtet))

    for (i = 0; i < 3; i++) {
      pa = org(spintet);
      pb = dest(spintet);
      if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) {
        break; // Found the edge.
      }
      enextself(spintet);
      eprevself(searchtet);
    }
    esymself(searchtet);
  }
  else { // dir == ACROSSEDGE;
    if (issubseg(searchtet)) {
      terminatetetgen(this, 2);
      if (misseg != NULL) {
        // Report a self_intersection.
        //bool intersect_flag = false;
        //point nearpt = dest(searchtet);
        ivf.iloc = ONVERTEX;
        // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX);
        int segidx = getfacetindex(*misseg);
        point p1 = segmentendpointslist[segidx*2];
        point p2 = segmentendpointslist[segidx*2+1];
        face parsentseg;
        //sdecode(point2sh(nearpt), parsentseg);
        tsspivot1(searchtet, parsentseg);
        int segidx2 = getfacetindex(parsentseg);
        if (segidx2 != segidx) {
          if (!b->quiet && !b->nowarning) {
            point p3 = segmentendpointslist[segidx2*2];
            point p4 = segmentendpointslist[segidx2*2+1];
            printf("Warning:  Two line segments are %s crossing.\n",
                   ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
            printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
            printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
          }
          //intersect_flag = true;
        } else {
          if (ivf.iloc == ONVERTEX) {
            terminatetetgen(this, 2); // This should not be possible.
          }
        }
        idir = (int) SELF_INTERSECT;
      } // if (misseg != NULL)
      return 0;
    }
  }

  if (!splitsegflag) {
    // Try to recover this segment by adding Steiner points near it.

    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;
    }

    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, splitsliverflag, 0)) {
          success = 1;
        }
      } else if (dir == ACROSSEDGE) {
        // PLC check.
        if (issubseg(searchtet)) {
          terminatetetgen(this, 2);
        }
        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, splitsliverflag, 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, splitsliverflag, 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.
        }
      } else {
        terminatetetgen(this, 2);
      }

      delete [] abtets;

      if (success && (misseg != NULL)) {
        // Add the missing segment back to the recovering list.
        subsegstack->newindex((void **) &paryseg);
        *paryseg = *misseg;
      }
      
      if (success) {
        return 1;
      }
    } // if (endi > 0)

    return 0;
  } // if (!splitsegflag)

  if (b->verbose > 3) {
    printf("      Recover segment (%d, %d) by splitting it.\n",
           pointmark(startpt), pointmark(endpt));
  }
  steinerpt = NULL;

  if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2
    if (add_steinerpt_in_segment(misseg, 3, idir)) {
      return 1;
    }
    if (idir == SELF_INTERSECT) {
      return 0;
    }
    sesymself(*misseg);
    if (add_steinerpt_in_segment(misseg, 3, idir)) {
      return 1;
    }
    sesymself(*misseg);
    if (idir == SELF_INTERSECT) {
      return 0;
    }
  }


  // Let the face [a,b,d] be the first intersecting face of the segment
  //   [startpt, endpt]. We add the interseting point.
  REAL ip[3], u;
  point2tetorg(startpt, searchtet);
  dir = finddirection(&searchtet, endpt);
  if (dir == ACROSSVERT) {
    if (dest(searchtet) == endpt) {
      // This edge exists.
      if (misseg != NULL) {
        // Add the missing segment back to the recovering list.
        subsegstack->newindex((void **) &paryseg);
        *paryseg = *misseg;
      }
      return 1;
    } else {
      // This should be a self-intersection.
      if (misseg != NULL) {
        terminatetetgen(this, 2);
        // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX);
        idir = (int) SELF_INTERSECT;
      }
      return 0;
    }
  }

  enextself(searchtet);
  pa = org(searchtet);
  pb = dest(searchtet);
  pd = oppo(searchtet);

  // Calculate the intersection of the face [a,b,d] and the segment.
  //planelineint(pa, pb, pd, startpt, endpt, ip, &u);

  point fpt[3], ept[2];
  sort_3pts(pa, pb, pd, fpt);
  sort_2pts(startpt, endpt, ept);
  planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u);

  if ((u > 0) && (u < 1)) {
    // Create a Steiner point.
    makepoint(&steinerpt, FREESEGVERTEX);
    for (i = 0; i < 3; i++) steinerpt[i] = ip[i];
    
    // for create_a_shorter_edge().
    setpoint2sh(steinerpt, sencode(*misseg));
    
    esymself(searchtet); // The crossing face/edge.
    spivot(*misseg, splitsh);
    if (dir == ACROSSFACE) {
      //ivf.iloc = (int) ONFACE;
      ivf.refineflag = 4; // Check if the crossing face is removed.
    } else {
      //ivf.iloc = (int) ONEDGE;
      ivf.refineflag = 8; // Check if the crossing edge is removed.
    }
    ivf.iloc = (int) OUTSIDE; // do point location.
    ivf.refinetet = searchtet; // The crossing face/edge.
    ivf.bowywat = 1;
    // ivf.lawson = 0;
    ivf.lawson = 2; // Recover Delaunay after inserting this vertex.
    ivf.rejflag = 0;
    ivf.chkencflag = 0;
    ivf.sloc = (int) ONEDGE;
    ivf.sbowywat = 1; // split surface mesh separately, new subsegments are
                      //   pushed into "subsegstack".
    ivf.splitbdflag = 0;
    ivf.validflag = 1;
    ivf.respectbdflag = 1;
    ivf.assignmeshsize = b->metric;

    if (insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) {
      if (flipstack != NULL) {
        recoverdelaunay();
      }
      
      // 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;
    } else {
      // Check if this failure is due to a self-intersection.
      if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX)) {
        if (misseg != NULL) {
          // report_seg_vertex_intersect(misseg, nearpt, ivf.iloc);
          int segidx = getfacetindex(*misseg);
          point p1 = segmentendpointslist[segidx*2];
          point p2 = segmentendpointslist[segidx*2+1];
          bool intersect_flag = false;
          point nearpt = org(searchtet);
          if (!issteinerpoint(nearpt)) {
            // 'nearpt' is an input vertex.
            if (!b->quiet && !b->nowarning) {
              point tmppt = NULL;
              if (is_segment(p1, nearpt)) tmppt = p1;
              else if (is_segment(p2, nearpt)) tmppt = p2;
              if (tmppt != NULL) {
                // Two input segments are nearly overlapping.
                printf("Warning:  Two line segments are %s overlapping.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt));
              } else {
                // An input vertex is very close to a segment.
                printf("Warning:  A vertex lies %s on a line segment.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  segment: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  vertex : [%d].\n", pointmark(nearpt));
              }
            } // if (!b->quiet && !b->nowarning)
            intersect_flag = true;
          } else {
            if (pointtype(nearpt) == FREESEGVERTEX) {
              // Check if two segments are nearly intersecting.
              face parsentseg;
              sdecode(point2sh(nearpt), parsentseg);
              int segidx2 = getfacetindex(parsentseg);
              if (segidx2 != segidx) {
                point p3 = segmentendpointslist[segidx2*2];
                point p4 = segmentendpointslist[segidx2*2+1];
                printf("Warning:  Two line segments are %s crossing.\n",
                       ivf.iloc == NEARVERTEX ? "nearly" : "exactly");
                printf("  1st: [%d,%d].\n", pointmark(p1), pointmark(p2));
                printf("  2nd: [%d,%d].\n", pointmark(p3), pointmark(p4));
                intersect_flag = true;
              }
            } else {
              // report other cases.
              // to do...
              terminatetetgen(this, 2);
            }
          }
          if (intersect_flag) {
            if (!b->quiet && !b->nowarning) {
              if (ivf.iloc == NEARVERTEX) {
                double dd = distance(steinerpt, nearpt);
                double new_dd = minedgelength - dd / longest;
                double new_eps = new_dd / longest;
                printf("You can ignore this warning by using -T%e (default is %e) option.\n",
                       new_eps, b->epsilon);
                printf("  This will allow a short edge (len = %.17g) (default limit is %g).\n",
                       dd, minedgelength);
              }
            }
            // A self-intersection is detected.
            idir = (int) SELF_INTERSECT;
          } // if (intersect_flag)
        } // if (misseg != NULL)
      } // if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX))

      // The vertex is not inserted.
      pointdealloc(steinerpt);
      steinerpt = NULL;
    }
  } // if ((u > 0) && (u < 1))

  return 0; // Failed to reocver this segment.

  // [2020-05-02] The following code is skipped.
  if (steinerpt == NULL) {
    // 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.
    spivot(*misseg, splitsh);
    ivf.iloc = (int) OUTSIDE;
    ivf.bowywat = 1;
    //ivf.lawson = 0;
    ivf.lawson = 2; // do flip to recover locally Delaunay faces.
    ivf.rejflag = 0;
    ivf.chkencflag = 0;
    ivf.sloc = (int) ONEDGE;
    ivf.sbowywat = 1; // mesh surface separately
    ivf.splitbdflag = 0;
    ivf.validflag = 1;
    ivf.respectbdflag = 1;
    ivf.assignmeshsize = b->metric; 
    if (insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) {
      if (flipstack != NULL) {
        recoverdelaunay();
      }
    } else {
      terminatetetgen(this, 2);
    }
  } // 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, idir;
  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 != NULL) {
      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, &sseg, &searchtet, 0, idir)) {
      success = 1;
    } else {
      // Try to recover it from the other direction.
      if ((idir != (int) SELF_INTERSECT) &&
          recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0, idir)) {
        success = 1;
      }
    }

    
    if (!success && fullsearch) {
      if ((idir != (int) SELF_INTERSECT) &&
          recoveredgebyflips(startpt, endpt, &sseg, &searchtet, fullsearch, idir)) {
        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 ((idir != (int) SELF_INTERSECT) && (steinerflag > 0)) {
        // Try to recover the segment but do not split it.
        if (add_steinerpt_to_recover_edge(startpt, endpt, &sseg, 0, 0, idir)) {
          success = 1;
        }
        if (!success && (idir != (int) SELF_INTERSECT) && (steinerflag > 1)) {
          // Split the segment.
          if (add_steinerpt_to_recover_edge(startpt, endpt, &sseg, 1, 0, idir)) {
            success = 1;
          }
        }
      }
    
      if (!success) {
        if (idir != (int) SELF_INTERSECT) {
          if (misseglist != NULL) {
            // Save this segment (recover it later).
            misseglist->newindex((void **) &paryseg);
            *paryseg = sseg;
          }
        } else {
          // Save this segment (do not recover it again).
          if (skipped_segment_list == NULL) {
            skipped_segment_list = new arraypool(sizeof(badface), 10);
          }
          badface *bf;
          skipped_segment_list->newindex((void **) &bf);
          bf->init();
          bf->ss = sseg;
          bf->forg  =  sorg(sseg);
          bf->fdest = sdest(sseg);
          bf->key = (double) shellmark(sseg);
          smarktest3(sseg);
          // Save all subfaces at this segment, do not recover them later.
          if (skipped_facet_list == NULL) {
            skipped_facet_list = new arraypool(sizeof(badface), 10);
          }
          face neighsh, spinsh;
          bf->ss.shver = 0;
          spivot(bf->ss, neighsh);
          spinsh = neighsh;
          while (spinsh.sh != NULL) {
            skipped_facet_list->newindex((void **) &bf);
            bf->init();
            bf->ss = spinsh;
            bf->forg  = (point) spinsh.sh[3];
            bf->fdest = (point) spinsh.sh[4];
            bf->fapex = (point) spinsh.sh[5];
            bf->key = (double) shellmark(spinsh);
            smarktest3(spinsh); // do not recover it.
            spivotself(spinsh);
            if (spinsh.sh == neighsh.sh) break;
          }
        }
      } // if (!success)
    }

  } // 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.                           //
//                                                                            //
// 'pa', 'pb', and 'pc' are the three vertices of this face.  This routine    //
// tries to recover it in the tetrahedral mesh. It is assumed that the three  //
// edges, i.e., pa->pb, pb->pc, and pc->pa all exist.                         //
//                                                                            //
// If the face is recovered, it is returned by 'searchtet'.                   //
//                                                                            //
// If 'searchsh' is not NULL, it is a subface to be recovered.  Its vertices  //
// must be pa, pb, and pc.  It is mainly used to check self-intersections.    //
// Another use of this subface is to split it when a Steiner point is found   //
// inside this subface.                                                       //
//                                                                            //
//============================================================================//

int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, 
                                   face *searchsh, triface* searchtet,
                                   int &dir, point *p1, point *p2)
{
  triface spintet, flipedge;
  point pd, pe;
  flipconstraints fc;
  int types[2], poss[4], intflag;
  int success;
  int t1ver; 
  int i, j;


  fc.fac[0] = pa;
  fc.fac[1] = pb;
  fc.fac[2] = pc;
  fc.checkflipeligibility = 1;
  
  dir = (int) DISJOINT;
  success = 0;

  for (i = 0; i < 3 && !success; i++) {
    while (1) {
      // Get a tet containing the edge [a,b].
      point2tetorg(fc.fac[i], *searchtet);
      finddirection(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);
          }
          dir = (int) SHAREFACE;
          success = 1;
          break;
        }
        fnextself(spintet);
        if (spintet.tet == searchtet->tet) break;
      } // while (1)

      if (success) break;

      // The face is missing. Try to recover it.
      flipedge.tet = NULL;
      // 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, NULL, 1, types, poss);
          if (intflag > 0) {
            // By the assumption that all edges of the face exist, they can
            //   only intersect at a single point.
            if (intflag == 2) {
              // Go to the edge [d,e].
              edestoppo(spintet, flipedge); // [d,e,a,b]
              if (searchsh != NULL) {
                // Check the intersection type.
                dir = types[0]; // return this value.
                if ((types[0] == (int) ACROSSFACE) || 
                    (types[0] == (int) ACROSSEDGE)) {
                  // Check if [e,d] is a segment.
                  if (issubseg(flipedge)) {
                    // This subface intersects with a segment.
                    if (!b->quiet && !b->nowarning) {
                      if (!b->quiet && !b->nowarning) {
                        printf("Warning:  A segment and a facet intersect.\n");
                        face sseg; tsspivot1(flipedge, sseg);
                        int segidx = getfacetindex(sseg);
                        point p1 = segmentendpointslist[segidx*2];
                        point p2 = segmentendpointslist[segidx*2+1];
                        printf("  segment: [%d,%d] tag(%d).\n",
                               pointmark(p1), pointmark(p2), shellmark(sseg));
                        point *ppt = (point *) &(searchsh->sh[3]);
                        printf("  facet triangle: [%d,%d,%d] tag(%d)\n",
                               pointmark(ppt[0]), pointmark(ppt[1]),
                               pointmark(ppt[2]), shellmark(*searchsh));
                      }
                    }
                    dir = (int) SELF_INTERSECT;
                    return 0; // Found a self-intersection.
		          } else {
				    // Check if [e,d] is an edge of a subface.
					triface chkface = flipedge;
					while (1) {
					  if (issubface(chkface)) break;
					  fsymself(chkface);
					  if (chkface.tet == flipedge.tet) break;
					}
					if (issubface(chkface)) {
                      if (searchsh != NULL) {
                        // Two subfaces are intersecting.
                        if (!b->quiet && !b->nowarning) {
                          printf("Warning:  Found two facets intersect.\n");
                          point *ppt = (point *) &(searchsh->sh[3]);
                          printf("  1st facet triangle: [%d,%d,%d] tag(%d)\n",
                                 pointmark(ppt[0]), pointmark(ppt[1]),
                                 pointmark(ppt[2]), shellmark(*searchsh));
                          face fa; tspivot(chkface, fa);
                          ppt = (point *) &(fa.sh[3]);
                          printf("  2nd facet triangle: [%d,%d,%d] tag(%d)\n",
                                 pointmark(ppt[0]), pointmark(ppt[1]),
                                 pointmark(ppt[2]), shellmark(fa));
                        }
                        dir = (int) SELF_INTERSECT;
                      }
                      return 0; // Found a self-intersection.
					}
				  }
				} else if (types[0] == TOUCHFACE) {
                  // This is possible when a Steiner point was added on it.
				  point touchpt, *parypt;
                  if (poss[1] == 0) {
                    touchpt = pd; // pd is a coplanar vertex.
                  } else {
                    touchpt = pe; // pe is a coplanar vertex.
                  }
                  if (!issteinerpoint(touchpt)) {
                    if (!b->quiet && !b->nowarning) {
                      printf("Warning:  A vertex lies on a facet.\n");
                      printf("  vertex : [%d]\n", pointmark(touchpt));
                      point *ppt = (point *) &(searchsh->sh[3]);
                      printf("  facet triangle: [%d,%d,%d] tag(%d)\n",
                             pointmark(ppt[0]), pointmark(ppt[1]),
                             pointmark(ppt[2]), shellmark(*searchsh));
                    }
                    dir = (int) SELF_INTERSECT;
                    return 0;
                  } else if (pointtype(touchpt) == FREESEGVERTEX) {
                    if (!b->quiet && !b->nowarning) {
                      printf("Warning:  A segment and a facet intersect.\n");
                      face sseg;
                      sdecode(point2sh(touchpt), sseg);
                      int segidx = getfacetindex(sseg);
                      point p1 = segmentendpointslist[segidx*2];
                      point p2 = segmentendpointslist[segidx*2+1];
                      printf("  segment: [%d,%d] tag(%d).\n",
                             pointmark(p1), pointmark(p2), shellmark(sseg));
                      point *ppt = (point *) &(searchsh->sh[3]);
                      printf("  facet triangle: [%d,%d,%d] tag(%d)\n",
                             pointmark(ppt[0]), pointmark(ppt[1]),
                             pointmark(ppt[2]), shellmark(*searchsh));
                    }
                    dir = (int) SELF_INTERSECT;
                    return 0;
                  } else if (pointtype(touchpt) == FREEFACETVERTEX) {
                    if (!b->quiet && !b->nowarning) {
                      printf("Warning:  Found two facets intersect.\n");
                      point *ppt = (point *) &(searchsh->sh[3]);
                      printf("  1st facet triangle: [%d,%d,%d] tag(%d)\n",
                             pointmark(ppt[0]), pointmark(ppt[1]),
                             pointmark(ppt[2]), shellmark(*searchsh));
                      face fa;
                      sdecode(point2sh(touchpt), fa);
                      ppt = (point *) &(fa.sh[3]);
                      printf("  2nd facet triangle: [%d,%d,%d] tag(%d)\n",
                             pointmark(ppt[0]), pointmark(ppt[1]),
                             pointmark(ppt[2]), shellmark(fa));
                    }
                    dir = (int) SELF_INTERSECT;
                    return 0;
                  } else 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. A 1-to-3 flip.
                    setpointtype(touchpt, FREEFACETVERTEX);
                    sinsertvertex(touchpt, searchsh, NULL, 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] != NULL) {
                        subfacstack->newindex((void **) &parysh);
                        *parysh = checksh;
                      }
                    }
                    // Delete the old subfaces in sC(p).
                    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 = NULL; // It has been split.
					return 1;
                  } else {
				    // Other cases may be due to a bug or a PLC error.
					//return report_selfint_face(pa, pb, pc, searchsh, &flipedge,
                    //                           intflag, types, poss);
                    terminatetetgen(this, 2); // to debug...
                    dir = (int) SELF_INTERSECT;
                    return 0; // Found a self-intersection.
				  }
                } else {
                  // The other intersection types: ACROSSVERT, TOUCHEDGE, 
                  // SHAREVERTEX should not be possible or due to a PLC error.
                  //return report_selfint_face(pa, pb, pc, searchsh, &flipedge,
                  //                           intflag, types, poss);
                  terminatetetgen(this, 2); // to report
                  dir = (int) SELF_INTERSECT;
                  return 0;
                }
              } // if (searchsh != NULL)
            } else { // intflag == 4. Coplanar case.
              // Found a mesh edge is coplanar with this subface.
              // It migh be caused by a self-intersection.
              terminatetetgen(this, 2); // report this bug
            }
            break;
          } // if (intflag > 0)
        }
        fnextself(spintet);
        if (spintet.tet == searchtet->tet) {
          terminatetetgen(this, 2);
        }
      } // while (1)
      // Try to flip the edge [d,e].
      // Remember a crossing edge.
      *p1 =  org(flipedge);
      *p2 = dest(flipedge);

      if (removeedgebyflips(&flipedge, &fc) == 2) {
        // A crossing edge is removed.
        continue; 
      }
      
      // Unable to remove a crossing edge of this face.
      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 cross_e1 = NULL, cross_e2 = NULL; // endpoints of a crossing edge.
  point steinerpt;
  insertvertexflags ivf;
  int success, dir;
  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] == NULL) continue; // Skip a dead subface.
    if (smarktest3ed(searchsh)) continue; // Skip a self-intersected subface.

    stpivot(searchsh, neightet);
    if (neightet.tet != NULL) 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)));
    }
    dir = (int) DISJOINT; // No self intersection is detected.

    // 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 != NULL) {
        // Check if this segment exist.
        sstpivot1(bdsegs[i], searchtet);
        if (searchtet.tet == NULL) {
          // This segment is not recovered yet. Try to recover it.
          success = 0;
          startpt = sorg(searchsh);
          endpt = sdest(searchsh);
          if (recoveredgebyflips(startpt, endpt, &bdsegs[i], &searchtet, 0, dir)) {
            success = 1;
          } else {
            if ((dir != (int) SELF_INTERSECT) &&
                recoveredgebyflips(endpt, startpt, &bdsegs[i], &searchtet, 0, dir)) {
              success = 1;
            }
          }
          if (success) {
            // Segment is recovered. Insert it.
            // Let the segment remember an adjacent tet.
            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);
                ssdissolve(neineish);
                spivot(neineish, neighsh);
                if (neighsh.sh != NULL) {
                  ssdissolve(neighsh);
                }
                sstpivot1(bdsegs[j], searchtet);
                spintet = searchtet;
                while (1) {
                  tssdissolve1(spintet);
                  fnextself(spintet);
                  if (spintet.tet == searchtet.tet) break;
                }
                shellfacedealloc(subsegs, bdsegs[j].sh);
              }
            } // j
            break; // i
          } // if (success) else
        } // if (searchtet.tet == NULL)
      } else {
        // This edge is not a segment.
        // Check whether it exists or not.
        success = 0;
        startpt = sorg(searchsh);
        endpt = sdest(searchsh);
        point2tetorg(startpt, searchtet);
        finddirection(&searchtet, endpt);
        if (dest(searchtet) == endpt) {
          success = 1; // Found this edge.
        } else {
          // The edge is missing. Try to recover it.
          if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0, dir)) {
            success = 1;
          } else {
            if ((dir != (int) SELF_INTERSECT) &&
                recoveredgebyflips(endpt, startpt, &searchsh, &searchtet, 0, dir)) {
              success = 1;
            }
          }
        }

        if (success) {
          // This edge exists.
          if (issubseg(searchtet)) {
            // A segment already exists at this edge!
            //terminatetetgen(this, 2); // to debug
            //dir = SELF_INTERSECT;
            // We contnue to recover this subface instead of reporting a
            //   SELF_INTERSECT event.
            // Eventually, we will find "a duplicated triangle" event.
          }
        }
        
        if (success && (dir != SELF_INTERSECT)) {
          // This edge exists.
          //if (!issubseg(searchtet)) {
            // Insert a temporary segment to protect this edge.
            makeshellface(subsegs, &(bdsegs[i]));
            setshvertices(bdsegs[i], startpt, endpt, NULL);
            smarktest2(bdsegs[i]); // It's a temporary segment.
            // Insert this segment into surface mesh.
            ssbond(searchsh, bdsegs[i]);
            spivot(searchsh, neighsh);
            if (neighsh.sh != NULL) {
              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);
                ssdissolve(neineish);
                spivot(neineish, neighsh);
                if (neighsh.sh != NULL) {
                  ssdissolve(neighsh);
                }
              sstpivot1(bdsegs[j], searchtet);
                spintet = searchtet;
                while (1) {
                  tssdissolve1(spintet);
                  fnextself(spintet);
                  if (spintet.tet == searchtet.tet) break;
                }
              shellfacedealloc(subsegs, bdsegs[j].sh);
            }
          } // j

          break;
        }
      }
      senextself(searchsh);
    } // i

    if (i == 3) {
      // All edges of this subface exist (or have been recovered).
      // Recover the subface.
      startpt = sorg(searchsh);
      endpt   = sdest(searchsh);
      apexpt  = sapex(searchsh);

      success = recoverfacebyflips(startpt, endpt, apexpt,&searchsh, &searchtet,
                                   dir, &cross_e1, &cross_e2);

      // Delete any temporary segment that has been created.
      for (j = 0; j < 3; j++) {
        if (smarktest2ed(bdsegs[j])) { 
          spivot(bdsegs[j], neineish);
            ssdissolve(neineish);
            spivot(neineish, neighsh);
            if (neighsh.sh != NULL) {
              ssdissolve(neighsh);
            }
          sstpivot1(bdsegs[j], neightet);
            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 != NULL) {
          // Face is recovered. Insert it.
          face chkface;
          tspivot(searchtet, chkface);
          if (chkface.sh == NULL) {
            tsbond(searchtet, searchsh);
            fsymself(searchtet);
            sesymself(searchsh);
            tsbond(searchtet, searchsh);
          } else {
            // A duplicated facet is found.
            if (shellmark(chkface) == shellmark(searchsh)) {
              if (!b->quiet && !b->nowarning) {
                point *ppt = (point *) &(searchsh.sh[3]);
                printf("Warning:  A duplicated triangle (%d,%d,%d) tag(%d) is ignored.\n",
                       pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2]),
                       shellmark(searchsh));
              }
              duplicated_facets_count++;
              smarktest3(searchsh); // do not recover it.
              sinfect(searchsh); // it is an igonred duplicated facet.
            } else {
              if (!b->quiet && !b->nowarning) {
                point *ppt = (point *) &(chkface.sh[3]);
                printf("Warning:  Two facets are overlapping at triangle (%d,%d,%d).\n",
                       pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2]));
                printf("  1st facet tag(%d).\n", shellmark(chkface));
                printf("  2nd facet tag(%d).\n", shellmark(searchsh));
              }
              dir = SELF_INTERSECT;
              success = 0;
            }
          }
        }
      } else {
        if ((dir != (int) SELF_INTERSECT) && steinerflag) {
          // Add a Steiner point at the barycenter of this subface.
          REAL ip[3], u;

          //planelineint(startpt, endpt, apexpt, cross_e1, cross_e2, ip, &u);

          point fpt[3], ept[2];
          sort_3pts(startpt, endpt, apexpt, fpt);
          sort_2pts(cross_e1, cross_e2, ept);
          planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u);

          makepoint(&steinerpt, FREEFACETVERTEX);
          if ((u > 0.) && (u < 1.)) {
            for (j = 0; j < 3; j++) steinerpt[j] = ip[j];
            // Make sure that this Steiner point is inside the subface.
            if (is_collinear_at(steinerpt, startpt, endpt) ||
                is_collinear_at(steinerpt, endpt, apexpt) ||
                is_collinear_at(steinerpt, apexpt, startpt)) {
              // Add the barycenter of this missing subface.
              for (j = 0; j < 3; j++) {
                steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
              }
              // Avoid creating a very skinny triangle
              if (is_collinear_at(steinerpt, startpt, endpt) ||
                  is_collinear_at(steinerpt, endpt, apexpt) ||
                  is_collinear_at(steinerpt, apexpt, startpt)) {
                terminatetetgen(this, 2);
              }
            }
          } else {
            // Add the barycenter of this missing subface.
            for (j = 0; j < 3; j++) {
              steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
            }
            // Avoid creating a very skinny triangle
            if (is_collinear_at(steinerpt, startpt, endpt) ||
                is_collinear_at(steinerpt, endpt, apexpt) ||
                is_collinear_at(steinerpt, apexpt, startpt)) {
              //assert(0); // to debug...
              terminatetetgen(this, 2);
            }
          }

          // for create_a_shorter_edge().
          setpoint2sh(steinerpt, sencode(searchsh));

          ivf.init();
          point2tetorg(startpt, searchtet); // Start from 'searchtet'.
          ivf.iloc = (int) OUTSIDE; // Need point location.
          ivf.bowywat = 1;
          ivf.lawson = 2; // do recover delaunay.
          ivf.rejflag = 0;
          ivf.chkencflag = 0;
          ivf.sloc = (int) ONFACE; // "searchsh" must be the subface.
          ivf.sbowywat = 1; // split subface mesh separately, new subfaces
                            // are pushed into "subfacestack".
          ivf.splitbdflag = 0;
          ivf.validflag = 1;
          ivf.respectbdflag = 1;
          ivf.assignmeshsize = b->metric;

          if (insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) {
            if (flipstack != NULL) {
              recoverdelaunay();
            }
              
            // Save this Steiner point (for removal).
            //   Re-use the array 'subvertstack'.
            subvertstack->newindex((void **) &parypt);
            *parypt = steinerpt;

            st_facref_count++;
            if (steinerleft > 0) steinerleft--;
              
            success = 1; // This subface has been split.
          } else {
            // Failed to insert this point.
            if (ivf.iloc == NEARVERTEX) {
              // Check if this subface is nearly "touched" by an existing
              //   vertex. If so, report an event.
              point chkpt = org(searchtet);
              REAL dist = distance(steinerpt, chkpt);
              if (dist < minedgelength) {
                if (!issteinerpoint(chkpt)) {
                  if (!b->quiet && !b->nowarning) { // -no -Q -W
                    printf("Warning:  A facet (%d,%d,%d) and a vertex %d are very close.\n",
                           pointmark(sorg(searchsh)), pointmark(sdest(searchsh)),
                           pointmark(sapex(searchsh)), pointmark(chkpt));
                    double dd = dist; // distance(steinerpt, nearpt);
                    //assert(dd > 0.);
                    //minedgelength = longest * b->epsilon;
                    //assert(dd < minedgelength);
                    double new_dd = minedgelength - dd / longest;
                    double new_eps = new_dd / longest;
                    printf("You can ignore this warning by using -T%e (default is %e) option.\n",
                           new_eps, b->epsilon);
                    printf("  This will allow a short edge (len = %.17g) (default limit is %g).\n",
                           dd, minedgelength);
                  }
                  dir = SELF_INTERSECT;
                }
              } else {
                // Report other types of possible (nearly) self-intersection.
                terminatetetgen(this, 2);
                dir = SELF_INTERSECT;
              }
            }

            if ((dir != SELF_INTERSECT) && (steinerflag >= 2)) {
              if (ivf.iloc == NULLCAVITY) {
                // Collect a list of bad quality tets which prevent the
                //   insertion of this Steiner point.
                terminatetetgen(this, 2);
                point2tetorg(startpt, searchtet);
                ivf.iloc = (int) OUTSIDE; // re-do point location.
                ivf.collect_inial_cavity_flag = 1;
                insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf);
              } else {
                terminatetetgen(this, 2); // report a bug.
              }
            } // if (steinerflag >= 2)

            pointdealloc(steinerpt);
            steinerpt = NULL;
            success = 0; // queue this subface.
          }
        } // if (steinerflag)
      }
    } else { // when i < 3
      // An edge (startpt, endpt) of this subface is missing.
      if ((dir != (int) SELF_INTERSECT) && (steinerflag > 0)) {
        // Split this edge by adding a Steiner point.
        // Find the first face/edge crossed by the edge (startpt, endpt).
        point2tetorg(startpt, searchtet);
        dir = finddirection(&searchtet, endpt);


        if (dir != (int) SELF_INTERSECT) {
          // Insert a Steiner point.
          REAL ip[3], u;

          enextself(searchtet);
          point pa =  org(searchtet);
          point pb = dest(searchtet);
          point pd = oppo(searchtet);

          //planelineint(pa, pb, pd, startpt, endpt, ip, &u);
          
          point fpt[3], ept[2];
          sort_3pts(pa, pb, pd, fpt);
          sort_2pts(startpt, endpt, ept);
          planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u);

          makepoint(&steinerpt, FREEFACETVERTEX);
          for (j = 0; j < 3; j++) steinerpt[j] = ip[j];

          ivf.init();

          ivf.refinetet = searchtet; // bakup the crossing face/edge.

          triface tmptet = searchtet;
          ivf.iloc = locate(steinerpt, &tmptet);

          if (ivf.iloc == ONVERTEX) {
            // the origin of tmptet is co-incident with this Steiner point.
            searchtet = tmptet;
          }
          //else if (ivf.iloc == ONFACE) {
          //  searchtet = tmptet;
          //} else if (ivf.iloc == ONEDGE) {
          //  searchtet = tmptet;
          //}
          else {
            //assert(0); // to debug...
            // Make sure that we can split the crossing edge/face (a,b,d).
            if (dir == ACROSSFACE) {
              ivf.iloc = (int) ONFACE;
              //ivf.refineflag = 4; // Check if the crossing face is removed.
            } else if (dir == ACROSSEDGE) {
              ivf.iloc = (int) ONEDGE;
              //ivf.refineflag = 8; // Check if the crossing edge is removed.
            } else {
              terminatetetgen(this, 2);
            }
            //ivf.iloc = (int) OUTSIDE; // do point location.
            //ivf.refinetet = searchtet; // The crossing face/edge.
          }

          ivf.bowywat = 1;
          ivf.lawson = 2; // do recover delaunay.
          ivf.rejflag = 0;
          ivf.chkencflag = 0;
          ivf.sloc = (int) ONEDGE; // "searchsh" must be the subedge.
          ivf.sbowywat = 1; // split subface mesh separately, new subfaces
                            // are pushed into "subfacestack".
          ivf.splitbdflag = 0;
          ivf.validflag = 1;
          ivf.respectbdflag = 1;
          ivf.assignmeshsize = b->metric;

          //if (steinerflag >= 2) {
            // Skip NEARVERTEX. This may create a very short edge.
            //ivf.ignore_near_vertex = 1;
          //}

          // searchsh may contain a missing segment.
          // After splitting this subface, this segment must also be split.
          //   the two missing subsegments are stored in "subsegstack".
          face misseg, *splitseg = NULL;
          sspivot(searchsh, misseg);
          if (misseg.sh != NULL) {
            splitseg = &misseg;
            setpointtype(steinerpt, FREESEGVERTEX); // default is FREEFACETVERTEX.
            // for create_a_shorter_edge()
            setpoint2sh(steinerpt, sencode(misseg));
          } else {
            // for create_a_shorter_edge()
            setpoint2sh(steinerpt, sencode(searchsh));
          }

          bool splitseg_flag = (splitseg != NULL);
          int bak_iloc = ivf.iloc; // for collect_initial_cavity

          if (insertpoint(steinerpt, &searchtet, &searchsh, splitseg, &ivf)) {
            if (flipstack != NULL) {
              recoverdelaunay();
            }

            // Save this Steiner point (for removal).
            //   Re-use the array 'subvertstack'.
            subvertstack->newindex((void **) &parypt);
            *parypt = steinerpt;

            if (splitseg_flag) {
              st_segref_count++;
            } else {
              st_facref_count++;
            }
            if (steinerleft > 0) steinerleft--;

            success = 1; // This subface has been split.
          } else {
            // Failed to insert this point.
            if (ivf.iloc == NEARVERTEX) {
              // Check if this subface is nearly "touched" by an existing
              //   vertex. If so, report an event.
              point chkpt = org(searchtet);
              REAL dist = distance(steinerpt, chkpt); // for reporting.
              if (!issteinerpoint(chkpt)) {
                if (!b->quiet && !b->nowarning) {
                  if (splitseg_flag) {
                    printf("Warning:  A segment (%d,%d) and a vertex %d are very close.\n",
                           pointmark(sorg(searchsh)), pointmark(sdest(searchsh)),
                           pointmark(chkpt));
                  } else {
                    printf("Warning:  A facet (%d,%d,%d) and a vertex %d are very close.\n",
                           pointmark(sorg(searchsh)), pointmark(sdest(searchsh)),
                           pointmark(sapex(searchsh)), pointmark(chkpt));
                  }
                  //printf("  Will result a vert short edge (len=%.17g) (< %.17g)\n",
                  //       dist, minedgelength);
                  double dd = dist; // distance(steinerpt, nearpt);
                  //assert(dd > 0.);
                  //minedgelength = longest * b->epsilon;
                  //assert(dd < minedgelength);
                  double new_dd = minedgelength - dd / longest;
                  double new_eps = new_dd / longest;
                  printf("You can ignore this warning by using -T%e (default is %e) option.\n",
                         new_eps, b->epsilon);
                  printf("  This will allow a short edge (len = %.17g) (default limit is %g).\n",
                         dd, minedgelength);
                }
                dir = SELF_INTERSECT;
              }
            }

            if ((dir != SELF_INTERSECT) && (steinerflag >= 2)) {
              success = 0; // queue this subface.
              // Failed to split a crossing edge/face.
              if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX)) {
                // Get the existing vertex (must be a Steiner point).
                if (dir == ACROSSEDGE) {
                  int idir;
                  if (add_steinerpt_to_recover_edge(startpt, endpt, NULL, 0, 1, idir)) {
                    // A Steiner point is inserted.
                    // Push this subface back to stack, to recover it again.
                    subfacstack->newindex((void **) &parysh);
                    *parysh = searchsh;
                    success = 1;
                  }
                } else if (dir == ACROSSFACE)  {
                  // to do...
                  terminatetetgen(this, 2);
                } else {
                  terminatetetgen(this, 2); // not possible.
                }
              } else if (ivf.iloc == NULLCAVITY) {
                // Collect a list of bad quality tets which prevent the
                //   insertion of this Steiner point.
                terminatetetgen(this, 2);
              } else {
                terminatetetgen(this, 2); // report a bug.
              }
            } // if (steinerflag >= 2)
            
            pointdealloc(steinerpt);
            steinerpt = NULL;
            //success = 0; // queue this subface.
          }
        } // if (dir != SELF_INTERSECT)
      } // if ((dir != SELF_INTERSECT) && steinerflag > 0)
    } // if (i == 2) else

    if (success) continue; // recover the next subface.

    if (dir == (int) SELF_INTERSECT) {
      // Found a self-intersection. This subface cannot be recovered.
      // Save it in a separate list, and remove it from the subface pool.
      if (skipped_facet_list == NULL) {
        skipped_facet_list = new arraypool(sizeof(badface), 10);
      }
      badface *bf;
      skipped_facet_list->newindex((void **) &bf);
      bf->init();
      bf->ss = searchsh;
      bf->forg  = (point) searchsh.sh[3];
      bf->fdest = (point) searchsh.sh[4];
      bf->fapex = (point) searchsh.sh[5];
      bf->key = (double) shellmark(searchsh);
      smarktest3(searchsh); // do not recover it later.
      continue; // recover the next subface.
    }

    // This subface is missing.
    if (steinerflag >= 2) {
      terminatetetgen(this, 2);
    } // if (steinerflag >= 2)

    // Save this subface to recover it later.
    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 opposing 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 != NULL) {
    // 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 != NULL) {
      tspivot(neightet, checksh);
      if (!sinfected(checksh)) {
        // Collect this subface (link edge).
        sinfect(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 != NULL) {
      // 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 neighbors 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 != NULL) {
          tspivot(neightet, checksh);
          if (!sinfected(checksh)) {
            // Collect this subface (link edge).
            sinfect(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 != NULL) {
            // 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 != NULL) {
    for (i = 0; i < vertlist->objects; i++) {
      parypt = (point *) fastlookup(vertlist, i);
      puninfect(*parypt);
    }
  }

  if (shlist != NULL) {
    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 (e1 == NULL || e2 == NULL) {
    return 0;
  }
  if ((pointtype(e1) == UNUSEDVERTEX) ||
      (pointtype(e2) == UNUSEDVERTEX)) {
    return 0;
  }

  // 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);
  arraypool *tetlist = cavebdrylist;

  // 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);
  tetlist->newindex((void **) &parytet);
  *parytet = searchtet;
  infect(neightet);
  tetlist->newindex((void **) &parytet);
  *parytet = neightet;

  done = 0;

  for (i = 0; (i < tetlist->objects) && !done; i++) {
    parytet = (triface *) fastlookup(tetlist, 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);
          tetlist->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    } // j
  } // i 

  // Uninfect the list of visited tets.
  for (i = 0; i < tetlist->objects; i++) {
    parytet = (triface *) fastlookup(tetlist, i);
    uninfect(*parytet);
  }
  tetlist->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 {
          terminatetetgen(this, 2);
        }
      } 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 = NULL, wrktets[4];
  triface searchtet, spintet, neightet;
  face parentsh, spinsh, checksh;
  face leftseg, rightseg, checkseg;
  point lpt = NULL, rpt = NULL, 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);
    leftseg.shver = 0;
    if (sdest(leftseg) == steinerpt) {
      senext(leftseg, rightseg);
      spivotself(rightseg);
      rightseg.shver = 0;
    } else {
      rightseg = leftseg;
      senext2(rightseg, leftseg);
      spivotself(leftseg);
      leftseg.shver = 0;
    }
    lpt = sorg(leftseg);
    rpt = sdest(rightseg);
    
    // Check if both leftseg and rightseg are recovered in tet mesh.
    sstpivot1(leftseg, neightet);
    if (neightet.tet == NULL) {
      return 0; // Do not remove this Steiner point.
    }
    sstpivot1(rightseg, neightet);
    if (neightet.tet == NULL) {
      return 0; // Do not remove this Steiner point.
    }
    
    if (b->verbose > 2) {
      printf("      Removing Steiner point %d in segment (%d, %d).\n",
             pointmark(steinerpt), pointmark(lpt), pointmark(rpt));

    }
  } else if (vt == FREEFACETVERTEX) {
    if (b->verbose > 2) {
      printf("      Removing Steiner point %d in facet.\n",
             pointmark(steinerpt));
    }
  } else if (vt == FREEVOLVERTEX) {
    if (b->verbose > 2) {
      printf("      Removing Steiner point %d in volume.\n",
             pointmark(steinerpt));
    }
  } else if (vt == VOLVERTEX) {
    if (b->verbose > 2) {
      printf("      Removing a point %d in volume.\n",
             pointmark(steinerpt));
    }
  } 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, NULL);
  cavetetlist->restart(); // This list may be re-used.
  if (cavetetvertlist->objects > 3l) {
    valence = reduceedgesatvertex(steinerpt, cavetetvertlist);
  } else {
    valence = cavetetvertlist->objects;
  }
  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);
      }
      i = 0; // Count the numbe of tet at the edge [p,lpt].
      neightet.tet = NULL; // 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 = NULL;
      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;
        }
      }
      // Note, we do not detach the three subfaces at p.
      // They will be removed within a 4-to-1 flip.
      loc = ONFACE;
      removeflag = 1;
    }
    //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);
      }
      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;
        }
		{
			// [2017-10-15] Check if the tet is inverted?
			point chkp1 = org(neightet);
			point chkp2 = apex(neightet);
			REAL chkori = orient3d(rpt, lpt, chkp1, chkp2);
			if (chkori >= 0.0) {
				// Either inverted or degenerated.
				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 surrounding 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.
          sstbond1(rightseg, searchtet);
          spintet = searchtet;
          while (1) {
            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;
  }

  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 != NULL) {
          spinsh = parentsh;
          while (1) {
            stpivot(spinsh, neightet);
            if (neightet.tet != NULL) {
              tsdissolve(neightet);
            }
            sesymself(spinsh);
            stpivot(spinsh, neightet);
            if (neightet.tet != NULL) {
              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].
    if (vt == FREEFACETVERTEX) {
      // [2018-03-08] Check if the last 4-to-1 flip is valid.
      // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a]
      triface checktet, chkface;
      for (i = 0; i < 3; i++) {
        enext(fliptets[i], checktet);
        esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p]
        int scount = 0; int k;
        for (k = 0; k < 3; k++) {
          esym(checktet, chkface);
          if (issubface(chkface)) scount++;
          enextself(checktet);
        }
        if (scount == 3) {
          break; // Found a tet which support a 3-to-1 flip.
        } else if (scount == 2) {
          // This is a strange configuration. Debug it.
          // Do not do this flip.
          delete [] fliptets;
          return 0;
        }
      }
      if (i == 3) {
        // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it.
        int scount = 0;
        for (i = 0; i < 3; i++) {
          eprev(fliptets[i], checktet);
          esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d]
          if (issubface(chkface)) scount++;
        }
        if (scount != 3) {
          // Do not do this flip.
          delete [] fliptets;
          return 0;
        }
      }
    } // if (vt == FREEFACETVERTEX)
    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.
      for (i = 3; i < 6; i++) {
        if (issubface(fliptets[i])) valence++;
      }
      if (valence > 0) {
        // 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];
      }
      // [2018-03-08] Check if the last 4-to-1 flip is valid.
      // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a]
      triface checktet, chkface;
      for (i = 0; i < 3; i++) {
        enext(fliptets[i], checktet);
        esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p]
        int scount = 0; int k;
        for (k = 0; k < 3; k++) {
          esym(checktet, chkface);
          if (issubface(chkface)) scount++;
          enextself(checktet);
        }
        if (scount == 3) {
          break; // Found a tet which support a 3-to-1 flip.
        } else if (scount == 2) {
          // This is a strange configuration. Debug it.
          // Do not do this flip.
          delete [] fliptets;
          return 0;
        }
      }
      if (i == 3) {
        // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it.
        int scount = 0;
        for (i = 0; i < 3; i++) {
          eprev(fliptets[i], checktet);
          esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d]
          if (issubface(chkface)) scount++;
        }
        if (scount != 3) {
          // Do not do this flip.
          delete [] fliptets;
          return 0;
        }
      }
    } // vt == FREEFACETVERTEX
    // 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;
    }
    // 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];
  }

  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;
    // Insert the new segment.
    point2tetorg(lpt, searchtet);
    finddirection(&searchtet, rpt);
    if (dest(searchtet) != rpt) {
      terminatetetgen(this, 2);
    }
    sstbond1(rightseg, searchtet);
    spintet = searchtet;
    while (1) {
      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 != NULL) {
        spinsh = parentsh;
        while (1) {
          if (sorg(spinsh) != lpt) {
            sesymself(spinsh);
          }
          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);
          }
          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++;
  }
  if (vt != VOLVERTEX) {
    // 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;
}

//============================================================================//
//                                                                            //
// 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;
  badface bf;
  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.
  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->min_max_aspectratio) {
            get_tetqual(pa, pb, pc, nextpt, &bf);
            val = 1.0 / bf.key;
          } else if (opm->min_max_dihedangle) {
            get_tetqual(pa, pb, pc, nextpt, &bf);
            maxcosd = bf.cent[0];
            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->min_max_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.
        terminatetetgen(this, 2);
      }
    }

    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 smoothed.
    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;
}

//============================================================================//
//                                                                            //
// suppressbdrysteinerpoint()    Suppress a boundary Steiner point            //
//                                                                            //
//============================================================================//

int tetgenmesh::suppressbdrysteinerpoint(point steinerpt)
{
  face parentsh, spinsh, *parysh;
  face leftseg, rightseg;
  point lpt = NULL, rpt = NULL;
  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);
      rightseg.shver = 0;
    } else {
      rightseg = leftseg;
      senext2(rightseg, leftseg);
      spivotself(leftseg);
      leftseg.shver = 0;
    }
    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);
    if (parentsh.sh != NULL) {
      // It is not a dangling segment.
      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 == NULL) 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; // no need to suppress it.
  }

  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] = NULL;

  // 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, NULL, caveshlist);
    // Calculate the searching vector.
    pa = sorg(*parysh);
    pb = sdest(*parysh);
    pc = sapex(*parysh);
    facenormal(pa, pb, pc, v1, 1, NULL);
    len = sqrt(dot(v1, v1));
    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, NULL);
      len = sqrt(dot(v2, v2));
      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);
            break;
          }
        }
      }
    } // j
    if (j == cavetetlist->objects) {
      break; // There is no intersection!! Debug is needed.
    }
    // 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);
          {
            // [2017-10-15] Rounding
            REAL lab = distance(pa, pb);
            REAL lbc = distance(pb, pc);
            REAL lca = distance(pc, pa);
            REAL lv = (lab + lbc + lca) / 3.0;
            REAL l3 = lv*lv*lv;
            if (fabs(ori) / l3 < 1e-8) ori = 0.0;
          }
          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] != NULL) {
        pointdealloc(newsteiners[i - 1]);
      }
    }
    delete [] newsteiners;
    cavesegshlist->restart();
    return 0;
  }

  // First insert Steiner points into the mesh.    
  // 'cavesegshlist' will be used by insertpoint().
  //int nfaces = cavesegshlist->objects;
  face *segshlist = new face[n];
  for (i = 0; i < cavesegshlist->objects; i++) {
    segshlist[i] = * (face *) fastlookup(cavesegshlist, i);    
  }
  cavesegshlist->restart();

  for (i = 0; i < n; i++) {    
    //assert(caveoldtetlist->objects == 0); 
    //assert(cavetetlist->objects == 0);
    parysh = &(segshlist[i]);
    // 'parysh' is the face [lpt, steinerpt, #].
    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, NULL, caveshlist);
    
    // Get all tets in this sector.
    for (int j = 0; j < cavetetlist->objects; j++) {
      neightet = * (triface *) fastlookup(cavetetlist, j);
      infect(neightet);
      caveoldtetlist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    cavetetlist->restart();
    caveshlist->restart();

    insertvertexflags ivf;
    searchtet = neightet; // No need point location.
    ivf.iloc = (int) INSTAR;  // No need point location.
    // The following are default options.
    //ivf.bowywat = 0;
    //ivf.lawson = 0;
    //ivf.validflag = 0; // no need to validate cavity.
    //ivf.chkencflag = 0; //chkencflag;
    ivf.assignmeshsize = b->metric; 
    if (ivf.assignmeshsize) {
      // Search the tet containing 'steinerpt' for size interpolation.
      locate(newsteiners[i], &searchtet);
    }

    // Insert the new point into the tetrahedralization T.
    // Note that T is convex (nonconvex = 0).
    if (insertpoint(newsteiners[i], &searchtet, NULL, NULL, &ivf)) {
      // The vertex has been inserted.
      st_volref_count++; 
      if (steinerleft > 0) steinerleft--;
      //return 1;
    } else {
      // Not inserted. 
      //assert(0);
      pointdealloc(newsteiners[i]);
      newsteiners[i] = NULL;
      break; //return 0;
    }
  } // i

  delete [] segshlist;

  if (i < n) {
    //assert(0); 
    delete [] newsteiners;
    return 0;  
  }

  // Now remove the Steiner point from the segment.
  if (!removevertexbyflips(steinerpt)) {
    //assert(0);
    delete [] newsteiners;
    return 0;
  }

  // We've removed a Steiner points.
  setpointtype(steinerpt, UNUSEDVERTEX);
  unuverts++;
  
  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] != NULL) {
      if (!removevertexbyflips(newsteiners[i])) {
        if (b->supsteiner_level > 0) { // Not -Y/0
          // Save it in subvertstack for removal.
          point *parypt;
          subvertstack->newindex((void **) &parypt);
          *parypt = newsteiners[i];
        }
        steinercount++;
      }
    }
  }

  b->fliplinklevel = bak_fliplinklevel;

  if (steinercount > 0) {
    if (b->verbose > 3) {
      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->supsteiner_level > 0) { // -Y/1
    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->supsteiner_level > 1) { // -Y/2
    // 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, NULL, NULL);
            // 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");
  }

  boundary_recovery_flag = 1;
  cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180. * PI);

  if (segmentendpointslist == NULL) {
    // We need segment adjacent information during flips.
    makesegmentendpointsmap();
  }


  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);

  // 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);
    }
  }

  //int bak_verbose = b->verbose;
  //if (b->verbose < 3) {
  //  b->verbose = 3; // debug...
  //}

  if (misseglist->objects > 0) {
    // Third, trying to recover segments by doing more flips (fullsearch)
    //   and adding Steiner points in the volume.

    if (b->verbose) {
      printf("  Recovering Delaunay.\n");
    }
    
    recoverdelaunay();
    
    if (b->verbose) {
      printf("  Recovering segments with Steiner points.\n");
    }

    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);
      recoversegments(misseglist, 0, 1); // no full search

      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;

    if (b->verbose) {
      printf("  Recovering Delaunay.\n");
    }
    
    recoverdelaunay();

    if (b->verbose) {
      printf("  Recovering segments with Steiner points.\n");
    }

    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, 2);
      recoversegments(misseglist, 0, 2); // no full search

      if (misseglist->objects < ms) {
        // The number of missing segments is reduced.
        continue;
      } else {
        break;
      }
    } // while (misseglist->objects > 0)

    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);
      }
    }

    // There may be un-recovered subsegments.
    if (misseglist->objects > 0l) {
      if (b->verbose) {
        printf("  !! %ld subsegments are missing.\n", misseglist->objects);
      }
    }
  }

  if (skipped_segment_list != NULL) {
    if (!b->quiet) {
      printf("  Skipped %ld segments due to intersections.\n",
             skipped_segment_list->objects);
    }
    delete skipped_segment_list;
  }


  //b->verbose = bak_verbose; // debug...

  if (st_segref_count > 0) {
    // Try to remove the Steiner points added in segments.
    if (b->verbose) {
      printf("  Suppressing %ld Steiner points in segments.\n", st_segref_count);
    }
    int bak_fliplinklevel = b->fliplinklevel;
    b->fliplinklevel = 20; // limit this value
    
    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)));
        }
      }
    }
    
    b->fliplinklevel = bak_fliplinklevel; // restore it.
    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; // this can be very slow.
            if (autofliplinklevel < 30) {
              b->fliplinklevel = 30;
            } else {
              b->fliplinklevel = autofliplinklevel + 30;
            }
          }
        } 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.

    if (b->verbose) {
      printf("  Recovering Delaunay.\n");
    }
    
    recoverdelaunay();
    
    if (b->verbose) {
      printf("  Recovering facets with Steiner points.\n");
    }
    
    while (misshlist->objects > 0) {
      ms = misshlist->objects;
      for (i = 0; i < misshlist->objects; i++) {
        subfacstack->newindex((void **) &parysh);
        *parysh = * (face *) fastlookup(misshlist, i);
      }
      misshlist->restart();
      
      recoversubfaces(misshlist, 1);
      
      if (misshlist->objects < ms) {
        continue;
      } else {
        break;
      }
    }
    
    if (b->verbose) {
      printf("  %ld (%ld) subfaces are recovered (missing).\n",
             subfaces->items - misshlist->objects, misshlist->objects);
      printf("  Added %ld Steiner points in facets.\n", st_facref_count);
    }
  }

  if (misshlist->objects > 0) {
    long bak_steiner = st_facref_count;
  
    if (b->verbose) {
      printf("  Recovering Delaunay.\n");
    }
    
    recoverdelaunay();
  
    if (b->verbose) {
      printf("  Recovering facets with Steiner points.\n");
    }
  
    while (misshlist->objects > 0) {
      ms = misshlist->objects;
      for (i = 0; i < misshlist->objects; i++) {
        subfacstack->newindex((void **) &parysh);
        *parysh = * (face *) fastlookup(misshlist, i);
      }
      misshlist->restart();
      
      recoversubfaces(misshlist, 2); // steinerflag = 2;
      
      if (misshlist->objects < ms) {
        continue;
      } else {
        break;
      }
    }

    if (subsegstack->objects > 0) {
      // Save unrecovered subsegments.
      triface neightet;
      face checkseg;
      for (i = 0; i < subsegstack->objects; i++) {
        checkseg = * (face *) fastlookup(subsegstack, i);
        if ((checkseg.sh == NULL) ||
            (checkseg.sh[3] == NULL)) continue;
        // Check if this subsegment is missing.
        sstpivot1(checkseg, neightet);
        if (neightet.tet != NULL) continue;
        // Save a missing subsegment.
        misseglist->newindex((void **) &paryseg);
        *paryseg = checkseg;
      }
      subsegstack->restart();
    } // if (subsegstack->objects > 0)

    if (b->verbose) {
      printf("  %ld (%ld) subfaces are recovered (missing).\n",
             subfaces->items - misshlist->objects, misshlist->objects);
      printf("  Added %ld Steiner points in facets.\n",
             st_facref_count - bak_steiner);
    }
  }

  // There may be un-recovered subsegments.
  if (misshlist->objects > 0l) {
    if (b->verbose) {
      printf("  !! %ld subfaces are missing.\n", misshlist->objects);
    }
    terminatetetgen(this, 2);
    // Save the list of missing subface.
    //missing_tri_list = new arraypool(sizeof(face), 8);
    //for (i = 0; i < misshlist->objects; i++) {
    //  missing_tri_list->newindex((void **) &parysh);
    //  *parysh = * (face *) fastlookup(misshlist, i);
    //}
    //misshlist->restart();
  }
  
  if (duplicated_facets_count > 0l) {
    if (b->verbose) {
      printf("  Deleting %ld duplicated facets.\n", duplicated_facets_count);
    }
    triface neightet, spintet;
    face faceloop, sfaces[256]; // *tmp_sfaces = NULL;
    face sseg;
    int snum, snum_limit = 256;
    int t1ver;
    subfaces->traversalinit();
    faceloop.sh = shellfacetraverse(subfaces);
    while (faceloop.sh != NULL) {
      if (sinfected(faceloop)) {
        // Delete an ignored duplicated subface.
        shellfacedealloc(subfaces, faceloop.sh);
      }
      if (!smarktest3ed(faceloop)) {
        faceloop.shver = 0;
        stpivot(faceloop, neightet);
        if (neightet.tet == NULL) {
          terminatetetgen(this, 2);
        }
        // Update the subface connections at its three edges.
        for (int k= 0; k < 3; k++) {
          sspivot(faceloop, sseg);
          if (sseg.sh != NULL) {
            ssbond(faceloop, sseg); // Update segment connection.
          }
          // Get all subfaces at this edge.
          snum = 0;
          spintet = neightet;
          do {
            if (issubface(spintet)) {
              tspivot(spintet, sfaces[snum++]);
              if (snum > snum_limit) {
                // Unlikely to happen.
                terminatetetgen(this, 2);
                //tmp_sfaces = new face[snum_limit * 2];
              }
            }
            fnextself(spintet);
          } while (spintet.tet != neightet.tet);
          // Re-create the face ring.
          for (int j = 0; j < snum - 1; j++) {
            sbond1(sfaces[j], sfaces[j+1]);
          }
          sbond1(sfaces[snum - 1], sfaces[0]);
          enextself(neightet);
          senextself(faceloop);
        } // k
      }
      faceloop.sh = shellfacetraverse(subfaces);
    }
  } // if (duplicated_facets_count > 0l)


  if (st_facref_count > 0) {
    // Try to remove the Steiner points added in facets.
    if (b->verbose) {
      printf("  Suppressing %ld Steiner points in facets.\n", st_facref_count);
    }
    int bak_fliplinklevel = b->fliplinklevel;
    b->fliplinklevel = 30; // limit this value
    
    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);
      }
    }
    
    b->fliplinklevel = bak_fliplinklevel;
    subvertstack->restart();
  }


  // There may be missing segments and subfaces.
  if (misseglist->objects > 0) {
    triface adjtet;
    face checkseg;
    for (i = 0; i < misseglist->objects; i++) {
      checkseg = * (face *) fastlookup(misseglist, i);
      // A saved missing segment might be split or recovered.
      if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) {
        continue; // it is split.
      }
      sstpivot1(checkseg, adjtet);
      if (adjtet.tet != NULL) {
        continue; // it is recovered.
      }
      // This is a missing segmemt.
      subsegstack->newindex((void **) &paryseg);
      *paryseg = checkseg;
    }
    if (subsegstack->objects > 0) {
      if (!b->quiet && !b->nowarning) {
        printf("Warning:  %ld segments are not recovered.\n", subsegstack->objects);
      }
      //assert(0); // to do...
      subsegstack->restart();
    }
  }


  if (bdrysteinerptlist->objects > 0) {
    if (b->verbose) {
      printf("  %ld Steiner points remained in boundary.\n",
             bdrysteinerptlist->objects);
    }
  } // if


  boundary_recovery_flag = 0;

  // Accumulate the dynamic memory.
  totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory +
                      bdrysteinerptlist->totalmemory);

  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 = NULL;
  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 *) NULL) {
    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 = NULL;
      randomsample(&(in->holelist[i]), &neightet);
      if (locate(&(in->holelist[i]), &neightet) != 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->hole_mesh && (b->hole_mesh_filename[0] != 0)) {
    // A hole mesh (***.ele) is given.
    //enum tetgenbehavior::objecttype object;
    char filebasename[256];
    strcpy(filebasename, b->hole_mesh_filename);
    //object = tetgenbehavior::MESH;
    if (!strcmp(&filebasename[strlen(filebasename) - 4], ".ele")) {
      filebasename[strlen(filebasename) - 4] = '\0';
      //object = tetgenbehavior::MESH;
    }
    bool hole_mesh_loaded = false;
    tetgenio io;
    if (io.load_node(filebasename)) {
      if (io.load_tet(filebasename)) {
        hole_mesh_loaded = true;
      }
    }
    if (hole_mesh_loaded) {
      if (b->verbose) {
        printf("  Adding hole tets from the mesh %s\n", b->hole_mesh_filename);
      }
      int count = 0, hcount = 0, scount = 0;
      int shift = io.firstnumber > 0 ? -1 : 0;
      double *p1, *p2, *p3, *p4;
      double searchpt[3];
      // Randomly select a tet.
      i = randomnation(io.numberoftetrahedra);
      //for (i = 0; i < io.numberoftetrahedra; i++) {
        int *idx = &(io.tetrahedronlist[i * 4]);
        p1 = &(io.pointlist[(idx[0]+shift)*3]);
        p2 = &(io.pointlist[(idx[1]+shift)*3]);
        p3 = &(io.pointlist[(idx[2]+shift)*3]);
        p4 = &(io.pointlist[(idx[3]+shift)*3]);
        for (j = 0; j < 3; j++) {
          searchpt[j] = (p1[j]+p2[j]+p3[j]+p4[j])/4.;
        }
        // Search the point.
        neightet.tet = NULL;
        if (locate(searchpt, &neightet) != OUTSIDE) {
          // The tet 'neightet' contain this point.
          if (!infected(neightet)) {
            infect(neightet);
            tetarray->newindex((void **) &parytet);
            *parytet = neightet;
            count++;
            // 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);
                  hcount++;
                } else {
                  tetarray->newindex((void **) &parytet);
                  count++;
                }
                *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;
                  hcount++;
                }
              }
              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;
                scount++;
              }
            }
          }
        }
      //} // i
      if (b->verbose) {
        printf("    Added %d hole tets, %d hull tet, %d hole subfaces\n",
               count, hcount, scount);
      }
    } // if (hole_mesh_loaded)
  }

  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 = NULL;
      randomsample(&(in->regionlist[i]), &neightet);
      if (locate(&(in->regionlist[i]), &neightet) != 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 = NULL;
      }
    }
  }

  // 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).
            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 ((regiontets[i].tet != NULL) && 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 = NULL;
      }
    }
  }

  // Collect vertices which point to infected tets. These vertices
  //   may get deleted after the removal of exterior 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 != NULL) {
    if ((pointtype(ptloop) != UNUSEDVERTEX) &&
        (pointtype(ptloop) != DUPLICATEDVERTEX)) {
      decode(point2tet(ptloop), neightet);
      if (infected(neightet)) {
        cavetetvertlist->newindex((void **) &parypt);
        *parypt = ptloop;
      }
      if ((!b->cdt || b->nobisect) && (b->supsteiner_level > 0)) { // -Y/1
        // 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;
    face segloop, *paryseg;
    point pa, pb, pc;
    long delsegcount = 0l;
 
    // Collect segments which point to infected tets. Some segments
    //   may get deleted after the removal of exterior tets.
    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != NULL) {
      sstpivot1(segloop, neightet);
      if (infected(neightet)) {
        subsegstack->newindex((void **) &paryseg);
        *paryseg = segloop;
      }
      segloop.sh = shellfacetraverse(subsegs);
    }

    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] == NULL) {
          // Since the boundary of the domain may not be a manifold, we
          //   find the adjacent hull face by traversing 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;

      for (i = 0; i < subfacstack->objects; i++) {
        parysh = (face *) fastlookup(subfacstack, i);
        if (i == 0) {
          if (b->verbose) {
            printf("Warning:  Removed an exterior face (%d, %d, %d) #%d\n",
                   pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
                   pointmark(sapex(*parysh)), shellmark(*parysh));
          }
        }
        // Dissolve this subface from face links.
        for (j = 0; j < 3; j++) {         
          spivot(*parysh, casingout);
          sspivot(*parysh, checkseg);
          if (casingout.sh != NULL) {
            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 != NULL) {
              // Make sure the segment does not connect to a dead one.
              ssbond(casingout, checkseg);
            }
          } else {
            if (checkseg.sh != NULL) {
              //if (checkseg.sh[3] != NULL) {
              if (delsegcount == 0) {
                if (b->verbose) {
                  printf("Warning:  Removed an exterior segment (%d, %d) #%d\n",
                         pointmark(sorg(checkseg)), pointmark(sdest(checkseg)),
                         shellmark(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);
      }
      subfacstack->restart();
    } // if (subfacstack->objects > 0l)

    if (subsegstack->objects > 0l) {
      for (i = 0; i < subsegstack->objects; i++) {
        paryseg = (face *) fastlookup(subsegstack, i);
        if (paryseg->sh && (paryseg->sh[3] != NULL)) {
          sstpivot1(*paryseg, neightet);
          if (infected(neightet)) {
            if (b->verbose) {
              printf("Warning:  Removed an exterior segment (%d, %d) #%d\n",
                     pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)),
                     shellmark(*paryseg));
            }
            shellfacedealloc(subsegs, paryseg->sh);
            delsegcount++;
          }
        }
      }
      subsegstack->restart();
    } // if (subsegstack->objects > 0l)

    if (delsegcount > 0) {
      if (b->verbose) {
        printf("  Deleted %ld segments.\n", delsegcount);
      }
    }

    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 {
              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'.
    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;

    arraypool *tmpary = new arraypool(sizeof(int), 8);
    int *paryint;

    // 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 != NULL) {
          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
          tmpary->newindex((void **) &paryint);
          *paryint = attr;
          regioncount++;
        } // if (regiontets[i/5].tet != NULL)
      } // i
    }

    // Set attributes for all tetrahedra.
    attr = maxattr + 1;
    tetrahedrons->traversalinit();
    tetloop.tet = tetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) NULL) {
      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
        tmpary->newindex((void **) &paryint);
        *paryint = attr;
        attr++; // Increase the attribute.
        regioncount++;
      }
      tetloop.tet = tetrahedrontraverse();
    }

    // Uninfect processed tets.
    tetrahedrons->traversalinit();
    tetloop.tet = tetrahedrontraverse();
    while (tetloop.tet != (tetrahedron *) NULL) {
      uninfect(tetloop);
      tetloop.tet = tetrahedrontraverse();
    }

    // Until here, every tet has a region attribute.
    subdomains = regioncount; // Remember it for output.
    subdomain_markers = new int[subdomains];
    for (i = 0; i < subdomains; i++) {
      paryint = (int *) fastlookup(tmpary, i);
      subdomain_markers[i] = *paryint;
    }
    delete tmpary;

    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 != NULL) {
    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 *) NULL) {
      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)
}

// [2018-07-30]
// Search a face with given indices (i,j,k).
// This function is only called when the default fast search fails.
// It is possible when there are non-manifold edges on the hull.
// On finish, tetloop return this face if it exists, otherwise, return 0.
int tetgenmesh::search_face(point pi, point pj, point pk, triface &tetloop)
{
  pinfect(pi);
  pinfect(pj);
  pinfect(pk);

  int t1ver;
  triface t, t1;
  point *pts, toppo;
  int pcount = 0;
  
  t.ver = t1.ver = 0;
  tetrahedrons->traversalinit();
  t.tet = tetrahedrontraverse();
  while (t.tet != NULL) {
    pts = (point *) t.tet;
    pcount = 0;
    if (pinfected(pts[4])) pcount++;
    if (pinfected(pts[5])) pcount++;
    if (pinfected(pts[6])) pcount++;
    if (pinfected(pts[7])) pcount++;
    
    if (pcount == 3) {
      // Found a tet containing this face.
      for (t.ver = 0; t.ver < 4; t.ver++) {
        toppo = oppo(t);
        if (!pinfected(toppo)) break;
      }
      int ii;
      for (ii = 0; ii < 3; ii++) {
        if (org(t) == pi) break;
        enextself(t);
      }
      if (dest(t) == pj) {
      } else {
        eprevself(t);
        fsymself(t);
      }
      break;
    }
    t.tet = tetrahedrontraverse();
  }

  puninfect(pi);
  puninfect(pj);
  puninfect(pk);

  if (t.tet != NULL) {
    tetloop = t;
    return 1;
  } else {
    return 0;
  }
}

int tetgenmesh::search_edge(point p0, point p1, triface &tetloop)
{
  triface t;
  int ii;

  tetrahedrons->traversalinit();
  t.tet = tetrahedrontraverse();
  while (t.tet != NULL) {
    for (ii = 0; ii < 6; ii++) {
      t.ver = edge2ver[ii];
      if (((org(t) == p0) && (dest(t) == p1)) ||
          ((org(t) == p1) && (dest(t) == p0))) {
        // Found the tet.
        tetloop = t;
        return 1;
      }
    }
    t.tet = tetrahedrontraverse();
  }

  tetloop.tet = NULL;
  return 0;
}

//============================================================================//
//                                                                            //
// 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 cosang_tol, cosang;
  REAL n1[3], n2[3];
  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.
    if (!(in->numberoftetrahedronattributes > 0)) {
      terminatetetgen(this, 2);
    }
  } 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];
  unuverts = in->numberofpoints; // All vertices are unused yet.
  //for (i = 0; i < in->numberofpoints + 1; i++) {
  for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) {
    ver2tetarray[i] = NULL;
  }

  // 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++]];
      if (pointtype(p[j]) == UNUSEDVERTEX) {
        setpointtype(p[j], VOLVERTEX); // initial type.
        unuverts--;
      }
    }
    // 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 *) NULL) {
        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 != NULL) {
        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] == NULL) {
              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 != NULL);
      } // if (checktet.tet != NULL)
    } // 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 *) NULL) {
    tptr = encode(tetloop);
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      if (tetloop.tet[tetloop.ver] == NULL) {
        // 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 == NULL) break;
            esymself(face2);
            if (apex(face2) == dummypoint) break;
            fsymself(face2);
          }
          if (face2.tet != NULL) {
            // Found an adjacent hull tet.
            esym(hulltet, face1);
            bond(face1, face2);
          }
          enextself(hulltet);
        }
      }
      // Create the point-to-tet map.
      setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr);
      // Clean the temporary used space.
      tetloop.tet[8 + tetloop.ver] = NULL;
    }
    tetloop.tet = tetrahedrontraverse();
  }

  hullsize = tetrahedrons->items - hullsize;

  // Subfaces will be inserted into the mesh. 
  if (in->trifacelist != NULL) {
    // 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 != NULL) {
        marker = in->trifacemarkerlist[i];
      } else {
        // Face markers are not available. Assume all of them are subfaces.
        marker = -1; // The default marker.
      }
      if (marker != 0) {
        idx = i * 3;
        for (j = 0; j < 3; j++) {
          p[j] = idx2verlist[in->trifacelist[idx++]];
        }
        // Search the subface.
        bondflag = 0;
        neighsh.sh = NULL;
        // Make sure all vertices are in the mesh. Avoid crash.
        for (j = 0; j < 3; j++) {
          decode(point2tet(p[j]), checktet);
          if (checktet.tet == NULL) 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 != NULL) {
                // 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) {
          if (neighsh.sh == NULL) {
            if (b->verbose > 1) {
              printf("Warning:  Searching subface #%d [%d,%d,%d] mark=%d.\n",
                     i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
                     pointmark(p[2]), marker);
            }
            // Search it globally.
            if (search_face(p[0], p[1], p[2], tetloop)) {
              bondflag = 1;
            }
          }
        }
        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);
          }
          setshellmark(subloop, marker);
          // Insert the subface into the mesh.
          tsbond(tetloop, subloop);
          fsymself(tetloop);
          sesymself(subloop);
          tsbond(tetloop, subloop);
        } else {
          if (neighsh.sh != NULL) {
            // The subface already exists. Only set its mark.
            setshellmark(neighsh, marker);
          } else {
            if (!b->quiet) {
              printf("Warning:  Subface #%d [%d,%d,%d] mark=%d is not found.\n",
                     i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
                     pointmark(p[2]), marker);
            }
          }
        } // if (bondflag)
      } // if (marker != 0)
    } // i
  } // if (in->trifacelist)

  // 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 *) NULL) {
    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
      tspivot(tetloop, neighsh);
      if (neighsh.sh == NULL) {
        bondflag = 0;
        fsym(tetloop, checktet);
        if (ishulltet(checktet)) {
          // A hull face.
          if (!b->convex) {
            bondflag = 1;  // Insert a hull subface.
          }
        } else {
          if (eextras > 0) {
            if (elemattribute(tetloop.tet, eextras - 1) !=
                elemattribute(checktet.tet, eextras - 1)) {
              bondflag = 1; // Insert 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, -1); // Default marker.
          // Insert the subface into the mesh.
          tsbond(tetloop, subloop);
          sesymself(subloop);
          tsbond(checktet, subloop);
        } // if (bondflag)
      } // if (neighsh.sh == NULL)
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Connect subfaces together. 
  subfaces->traversalinit();
  subloop.shver = 0;
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      spivot(subloop, neighsh);
      if (neighsh.sh == NULL) {
        // 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 != NULL) {
            // Do not connect itself.
            if (nextsh.sh != neighsh.sh) {
              // Link neighsh <= nextsh.
              sbond1(neighsh, nextsh);
              neighsh = nextsh;
            }
          }
          if (apex(tetloop) == q[2]) {
            break;
          }
        } // while (1)
      } // if (neighsh.sh == NULL)
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }


  // Segments will be introduced. 
  if (in->edgelist != NULL) {
    // 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 != NULL) {
        marker = in->edgemarkerlist[i];
      } else {
        // Edge markers are not available. Assume all of them are segments.
        marker = -1; // Default marker.
      }
      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 == NULL) break;
        }
        // Search the segment.
        bondflag = 0;
        if (j == 2) {
          if (getedge(p[0], p[1], &checktet)) {
            bondflag = 1;
          } else {
            if (b->verbose > 1) {
              printf("Warning:  Searching segment #%d [%d,%d] mark=%d.\n",
                i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), marker);
            }
            // Search it globally.
            if (search_edge(p[0], p[1], checktet)) {
              bondflag = 1;
            }
          }
        }
        if (bondflag > 0) {
          // Create a new segment.
          makeshellface(subsegs, &segloop);
          setshvertices(segloop, p[0], p[1], NULL);
          // 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 segment into the mesh.
          tetloop = checktet;
          q[2] = apex(checktet);
          subloop.sh = NULL;
          while (1) {
            tssbond1(tetloop, segloop);
            tspivot(tetloop, subloop);
            if (subloop.sh != NULL) {
              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)

  // 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_separate_ang_tol' (degree).
  cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI);
  subfaces->traversalinit();
  subloop.shver = 0;
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      sspivot(subloop, segloop);
      if (segloop.sh == NULL) {
        // Check if this edge is a segment.
        bondflag = 0;
        // Counter the number of subfaces at this edge.
        idx = 0;
        spivot(subloop, nextsh);        
        if (nextsh.sh != NULL) {
          nextsh = subloop;
          while (1) {
            idx++;
            spivotself(nextsh);
            if (nextsh.sh == subloop.sh) break;
          }
        } else {
          // There is only one subface at this edge.
          idx = 1;        
        }
        if (idx != 2) {
          // It's a non-manifold edge. Insert a segment.
          p[0] = sorg(subloop);
          p[1] = sdest(subloop);
          bondflag = 1;
        } else {
          // There are two subfaces at this edge.
          spivot(subloop, neighsh);
          if (shellmark(subloop) != shellmark(neighsh)) {
            // It's an interior interface. Insert a segment.
            p[0] = sorg(subloop);
            p[1] = sdest(subloop);
            bondflag = 1;
          } else {
            if (!b->convex) {
              // Check the dihedral angle formed by the two subfaces.
              p[0] = sorg(subloop);
              p[1] = sdest(subloop);
              p[2] = sapex(subloop);
              p[3] = sapex(neighsh);
              facenormal(p[0], p[1], p[2], n1, 1, NULL);
              facenormal(p[0], p[1], p[3], n2, 1, NULL);
              cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
              // Rounding.
              if (cosang > 1.0) cosang = 1.0;
              else if (cosang < -1.0) cosang = -1.0;  
              if (cosang > cosang_tol) {
                bondflag = 1;
              }
            }
          }
        }
        if (bondflag) {
          // Create a new segment.
          makeshellface(subsegs, &segloop);
          setshvertices(segloop, p[0], p[1], NULL);
          // 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, -1); // Default 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 != NULL) {
              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 == NULL)
      senextself(subloop);
    } // i
    subloop.sh = shellfacetraverse(subfaces);
  }

  // Remember the number of input segments.
  insegments = subsegs->items;

  if (!b->nobisect || checkconstraints) {
    // Mark Steiner points on segments and facets.
    //   - all vertices which remaining type FEACTVERTEX become
    //     Steiner points in facets (= FREEFACERVERTEX).
    //   - vertices on segment need to be checked.
    face* segperverlist;
    int* idx2seglist;
    face parentseg, nextseg;
    verttype vt;
    REAL area, len; // l1, l2;
    int fmarker;

    makepoint2submap(subsegs, idx2seglist, segperverlist);

    points->traversalinit();
    point ptloop = pointtraverse();
    while (ptloop != NULL) {
      vt = pointtype(ptloop);
      if (vt == VOLVERTEX) {
        setpointtype(ptloop, FREEVOLVERTEX);
        st_volref_count++;
      } else if (vt == FACETVERTEX) {
        setpointtype(ptloop, FREEFACETVERTEX);
        st_facref_count++;
      } else if (vt == RIDGEVERTEX) {
        idx = pointmark(ptloop) - in->firstnumber;
        if ((idx2seglist[idx + 1] - idx2seglist[idx]) == 2) {
          i = idx2seglist[idx];
          parentseg = segperverlist[i];
          nextseg = segperverlist[i + 1];
          sesymself(nextseg);
          p[0] = sorg(nextseg);
          p[1] = sdest(parentseg);
          // Check if three points p[0], ptloop, p[2] are (nearly) collinear.
          if (is_collinear_at(ptloop, p[0], p[1])) {
            // They are (nearly) collinear.
            setpointtype(ptloop, FREESEGVERTEX);
            // Connect nextseg and parentseg together at ptloop.
            senextself(nextseg);
            senext2self(parentseg);
            sbond(nextseg, parentseg);
            st_segref_count++;
          }
        }
      }
      ptloop = pointtraverse();
    }

    // Are there area constraints?
    if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) {
      // Set maximum area constraints on facets.
      for (i = 0; i < in->numberoffacetconstraints; i++) {
        fmarker = (int) in->facetconstraintlist[i * 2];
        area = in->facetconstraintlist[i * 2 + 1];
        subfaces->traversalinit();
        subloop.sh = shellfacetraverse(subfaces);
        while (subloop.sh != NULL) {
          if (shellmark(subloop) == fmarker) {
            setareabound(subloop, area);
          }
          subloop.sh = shellfacetraverse(subfaces);
        }
      }
    }

    // Are there length constraints?
    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
      // Set maximum length constraints on segments.
      int e1, e2;
      for (i = 0; i < in->numberofsegmentconstraints; i++) {
        e1 = (int) in->segmentconstraintlist[i * 3];
        e2 = (int) in->segmentconstraintlist[i * 3 + 1];
        len = in->segmentconstraintlist[i * 3 + 2];
        // Search for edge [e1, e2].
        idx = e1 - in->firstnumber;
        for (j = idx2seglist[idx]; j <  idx2seglist[idx + 1]; j++) {
          parentseg = segperverlist[j];
          if (pointmark(sdest(parentseg)) == e2) {
            setareabound(parentseg, len);
            break;
          }
        }
      }
    }

    delete [] idx2seglist;
    delete [] segperverlist;
  }


  // 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::scout_point(point searchpt, triface *searchtet, int randflag)
{
  if (b->verbose > 3) {
    printf("      Scout point %d.\n", pointmark(searchpt));
  }
  // randflag is not used.
  enum locateresult loc = OUTSIDE;
  int maxiter = 100, iter = 0;

  do {
    // 'searchtet' must be a valid tetrahedron.
    if (searchtet->tet == NULL) {
      // Randomly select a good starting tet.
      randomsample(searchpt, searchtet);
    }
  
    if (ishulltet(*searchtet)) {
      if ((recenttet.tet != NULL) && !ishulltet(recenttet)) {
        *searchtet = recenttet;
      }
    }
  
    if (ishulltet(*searchtet)) {
      int t1ver;
      searchtet->ver = 11;
      fsymself(*searchtet);
    }
  
    loc = locate_point_walk(searchpt, searchtet, 0); // encflg = 0.

    if (loc == OUTSIDE) {
      //randomsample(searchpt, searchtet);
      searchtet->tet = NULL;
    }

    iter++;
    if (iter < maxiter) break;
  } while (loc != OUTSIDE);

  if (loc == INTETRAHEDRON) {
    // Check if this vertex is nearly on subfacet.
    triface chktet = *searchtet;
    for (chktet.ver = 0; chktet.ver < 4; chktet.ver++) {
      if (issubface(chktet)) {
        point pa = org(chktet);
        point pb = org(chktet);
        point pc = org(chktet);
        REAL ori = orient3d(pa, pb, pc, searchpt);
        REAL averlen = (distance(pa, pb) +
                        distance(pb, pc) +
                        distance(pc, pa)) / 3.;
        REAL len3 = averlen * averlen * averlen;
        REAL ratio = (-ori) / len3;
        if (ratio < b->epsilon) {
          *searchtet = chktet;
          loc = ONFACE;
          break;
        }
      }
    }
  } // if (loc == INTETRAHEDRON)

  if (loc == ONFACE) {
    // Check if this vertex is nearly on a subsegment.
    triface chkface = *searchtet;
    for (int i = 0; i < 3; i++) {
      if (issubseg(chkface)) {
        REAL cosang = cos_interiorangle(searchpt, org(chkface), dest(chkface));
        if (cosang < cos_collinear_ang_tol) { // -p////179.9
          *searchtet = chkface;
          loc = ONEDGE;
          break;
        }
      }
      enextself(chkface);
    }
  } // if (loc == ONFACE)

  if (loc == ONEDGE) {
    // Check if this vertex is nearly on a vertex.
    triface chkedge = *searchtet;
    for (int i = 0; i < 2; i++) {
      REAL dd = distance(searchpt, org(chkedge));
      if (dd < minedgelength) {
        *searchtet = chkedge;
        loc = ONVERTEX;
        break;
      }
      esymself(chkedge);
    }
  }

  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]);
    // 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 != NULL) {
    // Search a tet in bgm which containing this point.
    searchtet.tet = NULL;
    iloc = bgm->scout_point(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,
                                         int rejflag)
{
  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.
  // Use Bowyer-Watson algorithm.
  ivf.bowywat = 1;
  ivf.lawson = 2; // do flip to recover Delaunay
  ivf.validflag = 1; // Validate the B-W cavity.
  ivf.rejflag = rejflag;
  ivf.chkencflag = 0; 
  ivf.sloc = (int) INSTAR;
  ivf.sbowywat = 3; 
  ivf.splitbdflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric;

  encseglist = new arraypool(sizeof(face), 8);
  encshlist = new arraypool(sizeof(badface), 8);
  searchtet.tet = NULL;

  // 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.
    ivf.iloc = scout_point(insertarray[i], &searchtet, randflag);

    // Decide the right type for this point.
    setpointtype(insertarray[i], FREEVOLVERTEX); // Default.
    splitsh.sh = NULL;
    splitseg.sh = NULL;
    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 != NULL) {
        flipconstraints fc;
        //fc.chkencflag = chkencflag;
        fc.enqflag = 2;
        lawsonflip3d(&fc);
        //unflipqueue->restart();
      }

      if (later_unflip_queue->objects > b->unflip_queue_limit) {
        recoverdelaunay();
      }
      
      // 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 (pointtype(insertarray[i]) != UNUSEDVERTEX) {
        setpointtype(insertarray[i], UNUSEDVERTEX);
      }
      unuverts++;

      encseglist->restart();
      encshlist->restart();
    }
  } // i

  if (later_unflip_queue->objects > 0) {
    recoverdelaunay();
  }

  delete encseglist;
  delete encshlist;
  encseglist = NULL;
  encshlist = NULL;

  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.
  int rejflag = 0;  // Do not check encroachment.
  if (b->metric) { // -m option.
    rejflag |= 4; // Reject it if it lies in some protecting balls.
  }

  insertconstrainedpoints(insertarray, arylen, rejflag);

  delete [] insertarray;
}

//============================================================================//
//                                                                            //
// meshcoarsening()    Deleting (selected) vertices.                          //
//                                                                            //
//============================================================================//

void tetgenmesh::collectremovepoints(arraypool *remptlist)
{
  point ptloop, *parypt;
  verttype vt;

  // If a mesh sizing function is given. Collect vertices whose mesh size
  //   is greater than its smallest edge length.
  if (b->metric) { // -m option
    REAL len, smlen;
    int i;
    points->traversalinit();
    ptloop = pointtraverse();
    while (ptloop != NULL) {
      // Do not remove a boundary vertex
      vt = pointtype(ptloop);
      if ((vt == RIDGEVERTEX) || /*(vt == ACUTEVERTEX) ||*/ (vt == FACETVERTEX) ||
          (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX) || (vt == UNUSEDVERTEX)) {
        ptloop = pointtraverse();
        continue;
      }
      if (ptloop[pointmtrindex] > 0) {
        // Get the smallest edge length at this vertex.
        getvertexstar(1, ptloop, cavetetlist, cavetetvertlist, NULL);
        parypt = (point *) fastlookup(cavetetvertlist, 0);
        smlen = distance(ptloop, *parypt);
        for (i = 1; i < cavetetvertlist->objects; i++) {
          parypt = (point *) fastlookup(cavetetvertlist, i);
          len = distance(ptloop, *parypt);
          if (len < smlen) {
            smlen = len;
          }
        }
        cavetetvertlist->restart();
        cavetetlist->restart();
        if (smlen < ptloop[pointmtrindex]) {
          pinfect(ptloop);
          remptlist->newindex((void **) &parypt);
          *parypt = ptloop;
        }
      }
      ptloop = pointtraverse();
    }
    if (b->verbose > 1) {
      printf("    Coarsen %ld oversized points.\n", remptlist->objects); 
    }
  }

  // If 'in->pointmarkerlist' exists, Collect vertices with markers '-1'.
  if (in->pointmarkerlist != NULL) {
    long bak_count = remptlist->objects;
    points->traversalinit();
    ptloop = pointtraverse();
    int index = 0;
    while (ptloop != NULL) {
      if (index < in->numberofpoints) {
        if (in->pointmarkerlist[index] == -1) {
          pinfect(ptloop);
          remptlist->newindex((void **) &parypt);
          *parypt = ptloop;
        }
      } else {
        // Remaining are not input points. Stop here.
        break; 
      }
      index++;
      ptloop = pointtraverse();
    }
    if (b->verbose > 1) {
      printf("    Coarsen %ld marked points.\n", remptlist->objects - bak_count); 
    }
  } // if (in->pointmarkerlist != NULL)

  if (b->coarsen_param > 0) { // -R1/#
    // Remove a coarsen_percent number of interior points.
    if (b->verbose > 1) {
      printf("    Coarsen %g percent of interior points.\n", 
             b->coarsen_percent * 100.0);
    }
    arraypool *intptlist = new arraypool(sizeof(point *), 10);
    // Count the total number of interior points.
    points->traversalinit();
    ptloop = pointtraverse();
    while (ptloop != NULL) {
      vt = pointtype(ptloop);
      if ((vt == VOLVERTEX) || (vt == FREEVOLVERTEX) || 
          (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX)) {
        intptlist->newindex((void **) &parypt);
        *parypt = ptloop;
      }
      ptloop = pointtraverse();
    }
    if (intptlist->objects > 0l) {
      // Sort the list of points randomly.
      point *parypt_i, swappt;
      int randindex, i;
      srand(intptlist->objects);
      for (i = 0; i < intptlist->objects; i++) {
        randindex = rand() % (i + 1); // randomnation(i + 1);
        parypt_i = (point *) fastlookup(intptlist, i); 
        parypt = (point *) fastlookup(intptlist, randindex);
        // Swap this two points.
        swappt = *parypt_i;
        *parypt_i = *parypt;
        *parypt = swappt;
      }
      int remcount = (int) ((REAL) intptlist->objects * b->coarsen_percent);
      // Return the first remcount points.
      for (i = 0; i < remcount; i++) {
        parypt_i = (point *) fastlookup(intptlist, i);
        if (!pinfected(*parypt_i)) {
          pinfected(*parypt_i);
          remptlist->newindex((void **) &parypt);
          *parypt = *parypt_i;
        }
      }
    }
    delete intptlist;
  }

  // Unmark all collected vertices.
  for (int i = 0; i < remptlist->objects; i++) {
    parypt = (point *) fastlookup(remptlist, i);
    puninfect(*parypt);
  }
}

void tetgenmesh::meshcoarsening()
{
  arraypool *remptlist;

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

  // Collect the set of points to be removed
  remptlist = new arraypool(sizeof(point *), 10);
  collectremovepoints(remptlist);

  if (remptlist->objects == 0l) {
    delete remptlist;
    return;
  }

  if (b->verbose) {
    if (remptlist->objects > 0l) {
      printf("  Removing %ld points...\n", remptlist->objects);
    }
  }

  point *parypt, *plastpt;
  long ms = remptlist->objects;
  int nit = 0; 
  int bak_fliplinklevel = b->fliplinklevel;
  b->fliplinklevel = -1;
  autofliplinklevel = 1; // Init value.
  int i;

  while (1) {
  
    if (b->verbose > 1) {
      printf("    Removing points [%s level = %2d] #:  %ld.\n", 
             (b->fliplinklevel > 0) ? "fixed" : "auto",
             (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
             remptlist->objects);
    }

    // Remove the list of points.
    for (i = 0; i < remptlist->objects; i++) {
      parypt = (point *) fastlookup(remptlist, i);
      if (removevertexbyflips(*parypt)) {
        // Move the last entry to the current place.
        plastpt = (point *) fastlookup(remptlist, remptlist->objects - 1);
        *parypt = *plastpt;
        remptlist->objects--;
        i--;
      }
    }

    if (remptlist->objects > 0l) {
      if (b->fliplinklevel >= 0) {
        break; // We have tried all levels.
      }
      if (remptlist->objects == ms) {
        nit++;
        if (nit >= 3) {
          // Do the last round with unbounded flip link level.
          b->fliplinklevel = 100000;
        }
      } else {
        ms = remptlist->objects;
        if (nit > 0) {
          nit--;
        }
      }
      autofliplinklevel+=b->fliplinklevelinc;
    } else {
      // All points are removed.
      break;
    }
  } // while (1)

  if (remptlist->objects > 0l) {
    if (b->verbose) {
      printf("  %ld points are not removed !\n", remptlist->objects);
    }
  }

  b->fliplinklevel = bak_fliplinklevel;
  delete remptlist;
}

//                                                                            //
//                                                                            //
//== reconstruct_cxx =========================================================//

//== refine_cxx ==============================================================//
//                                                                            //
//                                                                            //

//============================================================================//
//                                                                            //
// makesegmentendpointsmap()    Create a map from a segment to its endpoints. //
//                                                                            //
// The map is saved in the array 'segmentendpointslist'. The length of this   //
// array is twice the number of segments.  Each segment is assigned a unique  //
// index (starting from 0).                                                   //
//                                                                            //
//============================================================================//

void tetgenmesh::makesegmentendpointsmap()
{
  arraypool *segptlist;
  face segloop, prevseg, nextseg;
  point eorg, edest, *parypt;
  int segindex = 0, idx = 0;
  int i;

  if (b->verbose > 0) {
    printf("  Creating the segment-endpoints map.\n");
  }
  segptlist = new arraypool(2 * sizeof(point), 10);

  // for creating ridge_vertex-to-segment map;
  // The index might start from 0 or 1.
  idx_segment_ridge_vertex_list = new int[points->items + 2];
  for (i = 0; i < points->items + 2; i++) {
    idx_segment_ridge_vertex_list[i] = 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);
  segloop.shver = 0;
  while (segloop.sh != NULL) {
    senext2(segloop, prevseg);
    spivotself(prevseg);
    if (prevseg.sh == NULL) {
      eorg = sorg(segloop);
      edest = sdest(segloop);
      setfacetindex(segloop, segindex);
      senext(segloop, nextseg);
      spivotself(nextseg);
      while (nextseg.sh != NULL) {
        setfacetindex(nextseg, segindex);
        nextseg.shver = 0;
        if (sorg(nextseg) != edest) sesymself(nextseg);
        edest = sdest(nextseg);
        // Go the next connected subsegment at edest.
        senextself(nextseg);
        spivotself(nextseg);
      }
      segptlist->newindex((void **) &parypt);
      parypt[0] = eorg;
      parypt[1] = edest;
      segindex++;
      // for creating adj_ridge_vertex_list;
      idx_segment_ridge_vertex_list[pointmark(eorg)]++;
      idx_segment_ridge_vertex_list[pointmark(edest)]++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  if (b->verbose) {
    printf("  Found %ld segments.\n", segptlist->objects);
  }

  segmentendpointslist_length = segptlist->objects;
  segmentendpointslist = new point[segptlist->objects * 2];

  totalworkmemory += (segptlist->objects * 2) * sizeof(point *);

  for (i = 0; i < segptlist->objects; i++) {
    parypt = (point *) fastlookup(segptlist, i);
    segmentendpointslist[idx++] = parypt[0];
    segmentendpointslist[idx++] = parypt[1];
  }

  // Create the adj_ridge_vertex_list.
  int j = idx_segment_ridge_vertex_list[0], k;
  idx_segment_ridge_vertex_list[0] = 0;
  for (i = 0; i < points->items + 1; i++) {
    k = idx_segment_ridge_vertex_list[i+1];
    idx_segment_ridge_vertex_list[i+1] = idx_segment_ridge_vertex_list[i] + j;
    j = k;
  }

  //assert(i == points->items+1);
  int total_count = idx_segment_ridge_vertex_list[i] + 1;
  segment_ridge_vertex_list = new point[total_count];
  for (i = 0; i < segptlist->objects; i++) {
    eorg  = segmentendpointslist[i*2];
    edest = segmentendpointslist[i*2+1];
    j = pointmark(eorg);
    k = pointmark(edest);
    segment_ridge_vertex_list[idx_segment_ridge_vertex_list[j]] = edest; //eorg;
    segment_ridge_vertex_list[idx_segment_ridge_vertex_list[k]] = eorg;  //edest;
    idx_segment_ridge_vertex_list[j]++;
    idx_segment_ridge_vertex_list[k]++;
  }

  // Counters in idx_adj_ridge_vertex_list[] are shifted by 1.
  for (i = points->items; i >= 0; i--) {
    idx_segment_ridge_vertex_list[i+1] = idx_segment_ridge_vertex_list[i];
  }
  idx_segment_ridge_vertex_list[0] = 0;


  delete segptlist;
}

//============================================================================//
//                                                                            //
// set_ridge_vertex_protecting_ball()    Calculate the protecting ball for a  //
//                                       given ridge vertex.                  //
//                                                                            //
//============================================================================//

REAL tetgenmesh::set_ridge_vertex_protecting_ball(point ridge_pt)
{
  REAL rv = getpointinsradius(ridge_pt);
  if (rv == 0.) {
    REAL mindist = 1.e+30, dist;
    int idx = pointmark(ridge_pt);
    for (int i = idx_segment_ridge_vertex_list[idx];
           i < idx_segment_ridge_vertex_list[idx+1]; i++) {
      dist = distance(ridge_pt, segment_ridge_vertex_list[i]);
      if (mindist > dist) mindist = dist;
    }
    rv = mindist * 0.95; // mindist / 3.0; // refer to J. Shewchuk
    setpointinsradius(ridge_pt, rv);
  }
  return rv;
}

//============================================================================//
//                                                                            //
// get_min_diahedral_angle()    Calculate the minimum (interior) dihedral     //
//                              angle a given segment.                        //
//                                                                            //
//============================================================================//

REAL tetgenmesh::get_min_diahedral_angle(face* seg)
{
  triface adjtet, spintet;
  face startsh, neighsh;
  point pa, pb, pc1, pc2;
  REAL n1[3], n2[3];
  REAL n1len, n2len;
  REAL costheta; //, ori;
  REAL theta, sum_theta, minang = 2.0 * PI;
  int t1ver;
  
  pa = sorg(*seg);
  pb = sdest(*seg);
  spivot(*seg, startsh);
  if (startsh.sh == NULL) {
    // This segment is not connected by any facet.
    sstpivot1(*seg, adjtet);
    if (adjtet.tet != NULL) {
      // This segment is completely inside the volume.
      return 360.; // 2*pi.
    }
  } else {
    if (sorg(startsh) != pa) sesymself(startsh);
    stpivot(startsh, adjtet);
  }
  if (adjtet.tet == NULL) {
    // This segment is not inserted (recovered) yet.
    return 0.;
  }


  sum_theta = 0.;
  spintet = adjtet;
  while (true) {
    if (!ishulltet(spintet)) {
      // Increase the interior dihedral angle (sum_theta).
      pc1 = apex(spintet);
      pc2 = oppo(spintet);
      facenormal(pa, pb, pc1, n1, 1, NULL);
      facenormal(pa, pb, pc2, n2, 1, NULL);
      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);
      sum_theta += theta;
    }
    // Go to the next adjacent tetrahedron at this segment.
    fnextself(spintet);
    // Check if we meet a subface.
    tspivot(spintet, neighsh);
    if ((neighsh.sh != NULL) && (sum_theta > 0.)) {
      // Update the smallest dihedral angle.
      if (sum_theta < minang) minang = sum_theta;
      sum_theta = 0.; // clear it
    }
    if (spintet.tet == adjtet.tet) break;
  }
      
  double mindihedang = minang / PI * 180.;
  return mindihedang;
}

//============================================================================//
//                                                                            //
// get_min_angle_at_ridge_vertex()    Calculate the minimum face angle at a   //
//                                    given ridge vertex.                     //
//                                                                            //
//============================================================================//

REAL tetgenmesh::get_min_angle_at_ridge_vertex(face* seg)
{
  face startsh, spinsh, neighsh;
  point pa, pb, pc;
  REAL theta, sum_theta, minang = 2.0 * PI;
  
  pa = sorg(*seg);
  spivot(*seg, startsh);
  if (startsh.sh == NULL) {
    // This segment does not belong to any facet.
    return 360.; // 2*pi.
  } else {
    if (sorg(startsh) != pa) sesymself(startsh);
  }

  spinsh = startsh;
  while (spinsh.sh != NULL) {
    sum_theta = 0.;
    neighsh = spinsh;
    while (true) {
      pb = sdest(neighsh);
      pc = sapex(neighsh);
      theta = interiorangle(pa, pb, pc, NULL);
      sum_theta += theta;
      senext2self(neighsh);
      if (isshsubseg(neighsh)) break;
      spivotself(neighsh);
      if (sorg(neighsh) != pa) sesymself(neighsh);
    }
    if (sum_theta < minang) {
      minang = sum_theta;
    }
    // Go to the next facet at this segment.
    spivotself(spinsh);
    if (spinsh.sh == startsh.sh) break;
    if (spinsh.sh == NULL) break; // A single facet may happen.
    if (sorg(spinsh) != pa) sesymself(spinsh);
  }

  return minang / PI * 180.;
}

//============================================================================//
//                                                                            //
// create_segment_info_list()    Calculate the minimum dihedral angle at a    //
//                               a given segment.                             //
//                                                                            //
// segment_info_list = new double[segmentendpointslist_length * 4];           //
//  - [0] min_dihedral_angle (degree) at this segment,                        //
//  - [1] min_protect_cylinder_radius at this segment (for bookkeeping only), //
//  - [2] min_seg_seg_angle (degree) at its endpoint [0],                     //
//  - [3] min_seg_seg_angle (degree) at its endpoint [1].                     //
//                                                                            //
// This function must be called after makesegmentendpointsmap(). The number   //
//   of unique segments (segmentendpointslist_length) is calculated.          //
//                                                                            //
//============================================================================//

void tetgenmesh::create_segment_info_list()
{
  face min_dihedral_ang_seg;
  point min_face_ang_vertex;
  REAL min_dihedral_ang = 360.;
  REAL min_face_ang = 360.;
  
  if (b->verbose > 0) {
    printf("  Creating the segment_info_list.\n");
  }
  if (segment_info_list != NULL) {
    delete [] segment_info_list;
  }

  if (subsegs->items == 0) {
    return; // There is no segments.
  }

  int count = (segmentendpointslist_length + 1) * 4;
  segment_info_list = new double[count];
  for (int i = 0; i < count; i++) {
    segment_info_list[i] = 0.;
  }

  // Loop through the list of segments.
  face segloop;
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != NULL) {
    int segidx = getfacetindex(segloop);
    // Check if this segment has been already calulcated.
    double *values = &(segment_info_list[segidx * 4]);

    // The min_diahedral_angle at this segment is in (0, 2pi].
    if (values[0] == 0.) {
      // Get the smallest dihedral angle at this segment.
      values[0] = get_min_diahedral_angle(&segloop);
      if (values[0] < min_dihedral_ang) {
        min_dihedral_ang = values[0];
        min_dihedral_ang_seg = segloop;
      }
    }

    point *endpts = &(segmentendpointslist[segidx * 2]);

    for (int k = 0; k < 2; k++) {
      segloop.shver = 0;
      if (values[2+k] == 0.) {
        if (sorg(segloop) != endpts[k]) {
          sesymself(segloop);
        }
        if (sorg(segloop) == endpts[k]) {
          // Get the min face angle at vertex endpts[0].
          values[2+k] = get_min_angle_at_ridge_vertex(&segloop);
          if (values[2+k] < min_face_ang) {
            min_face_ang = values[2+k];
            min_face_ang_vertex = endpts[k];
          }
        }
      }
    }

    segloop.sh = shellfacetraverse(subsegs);
  }

  if (b->verbose) {
    printf("  min_dihedral angle = %g degree, at segment [%d,%d]\n",
           min_dihedral_ang, pointmark(sorg(min_dihedral_ang_seg)),
           pointmark(sdest(min_dihedral_ang_seg)));
    printf("  min face angle = %g degree, at vertex %d\n",
           min_face_ang, pointmark(min_face_ang_vertex));
  }
  
}

//============================================================================//
//                                                                            //
// makefacetverticesmap()    Create a map from facet to its vertices.         //
//                                                                            //
// All facets will be indexed (starting from 0).  The map is saved in two     //
// global arrays: 'idx2facetlist' and 'facetverticeslist'.                    //
//                                                                            //
//============================================================================//

void tetgenmesh::makefacetverticesmap()
{
  arraypool *facetvertexlist, *vertlist, **paryvertlist;
  face subloop, neighsh, *parysh, *parysh1;
  point pa, *ppt, *parypt;
  verttype vt;
  int facetindex, totalvertices;
  unsigned long max_facet_size = 0l;
  int max_facet_idx = 0;
  int i, j, k;

  if (b->verbose) {
    printf("  Creating the facet vertices map.\n");
  }

  facetvertexlist = new arraypool(sizeof(arraypool *), 10);
  facetindex = totalvertices = 0;

  // The index might start from 0 or 1.
  idx_ridge_vertex_facet_list = new int[points->items + 2];
  for (i = 0; i < points->items + 2; i++) {
    idx_ridge_vertex_facet_list[i] = 0;
  }

  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != NULL) {
    if (!sinfected(subloop)) {
      // A new facet. Create its vertices list.
      vertlist = new arraypool(sizeof(point *), 8);
      ppt = (point *) &(subloop.sh[3]);
      for (k = 0; k < 3; k++) {
        vt = pointtype(ppt[k]);
        //if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) {
        if (vt == RIDGEVERTEX) {
          pinfect(ppt[k]);
          vertlist->newindex((void **) &parypt);
          *parypt = ppt[k];
          // for creating ridge_vertex-to-facet map.
          idx_ridge_vertex_facet_list[pointmark(ppt[k])]++;
        }
      }
      sinfect(subloop);
      caveshlist->newindex((void **) &parysh);
      *parysh = subloop;
      for (i = 0; i < caveshlist->objects; i++) {
        parysh = (face *) fastlookup(caveshlist, i);
        setfacetindex(*parysh, facetindex);
        for (j = 0; j < 3; j++) {
          if (!isshsubseg(*parysh)) {
            spivot(*parysh, neighsh);
            if (!sinfected(neighsh)) {
              pa = sapex(neighsh);
              if (!pinfected(pa)) {
                vt = pointtype(pa);
                //if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) {
                if (vt == RIDGEVERTEX) {
                  pinfect(pa);
                  vertlist->newindex((void **) &parypt);
                  *parypt = pa;
                  // for creating ridge_vertex-to-facet map.
                  idx_ridge_vertex_facet_list[pointmark(pa)]++;
                }
              }
              sinfect(neighsh);
              caveshlist->newindex((void **) &parysh1);
              *parysh1 = neighsh;
            }
          }
          senextself(*parysh);
        }
      } // i
      totalvertices += (int) vertlist->objects;
      if (max_facet_size < vertlist->objects) {
        max_facet_size = vertlist->objects;
        max_facet_idx = facetindex;
      }
      // Uninfect facet vertices.
      for (k = 0; k < vertlist->objects; k++) {
        parypt = (point *) fastlookup(vertlist, k);
        puninfect(*parypt);
      }
      caveshlist->restart();
      // Save this vertex list.
      facetvertexlist->newindex((void **) &paryvertlist);
      *paryvertlist = vertlist;
      facetindex++;
    } 
    subloop.sh = shellfacetraverse(subfaces);
  }

  // All subfaces are infected. Uninfect them.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != NULL) {
    suninfect(subloop);
    subloop.sh = shellfacetraverse(subfaces);
  }

  if (b->verbose) {
    printf("  Found %ld facets. Max facet idx(%d), size(%ld)\n",
           facetvertexlist->objects, max_facet_idx, max_facet_size);
  }

  number_of_facets = facetindex;
  idx2facetlist = new int[facetindex + 1];
  facetverticeslist = new point[totalvertices];

  // create ridge_vertex-to-facet map.
  j = idx_ridge_vertex_facet_list[0]; //k;
  idx_ridge_vertex_facet_list[0] = 0;
  for (i = 0; i < points->items + 1; i++) {
    k = idx_ridge_vertex_facet_list[i+1];
    idx_ridge_vertex_facet_list[i+1] = idx_ridge_vertex_facet_list[i] + j;
    j = k;
  }

  int total_count = idx_ridge_vertex_facet_list[i] + 1;
  ridge_vertex_facet_list = new int[total_count];

  // Bookkeeping
  totalworkmemory += ((facetindex + 1) * sizeof(int) + 
                      totalvertices * sizeof(point *));

  idx2facetlist[0] = 0;
  for (i = 0, k = 0; i < facetindex; i++) {
    paryvertlist = (arraypool **) fastlookup(facetvertexlist, i);
    vertlist = *paryvertlist;
    idx2facetlist[i + 1] = (idx2facetlist[i] + (int) vertlist->objects);
    for (j = 0; j < vertlist->objects; j++) {
      parypt = (point *) fastlookup(vertlist, j);
      facetverticeslist[k] = *parypt;
      k++;
      // create ridge_vertex-to-facet map.
      int ridge_idx = pointmark(*parypt); // index of this ridge vertex
      // 'i' is the current facet index.
      ridge_vertex_facet_list[idx_ridge_vertex_facet_list[ridge_idx]] = i;
      // for the next facet index of this ridge vertex.
      idx_ridge_vertex_facet_list[ridge_idx]++;
    }
  }

  // Counters in idx_ridge_vertex_facet_list[] are shifted by 1.
  for (i = points->items; i >= 0; i--) {
    idx_ridge_vertex_facet_list[i+1] = idx_ridge_vertex_facet_list[i];
  }
  idx_ridge_vertex_facet_list[0] = 0;


  // Free the lists.
  for (i = 0; i < facetvertexlist->objects; i++) {
    paryvertlist = (arraypool **) fastlookup(facetvertexlist, i);
    vertlist = *paryvertlist;
    delete vertlist;
  }
  delete facetvertexlist;
}

//============================================================================//
//                                                                            //
// create_segment_facet_map()    Create the map from segments to adjacent     //
//                               facets.                                      //
//                                                                            //
//============================================================================//

void tetgenmesh::create_segment_facet_map()
{
  if (b->verbose > 0) {
    printf("  Creating the segment-to-facets map.\n");
  }
  if (idx_segment_facet_list != NULL) {
    delete [] idx_segment_facet_list;
    delete [] segment_facet_list;
  }

  face startsh, spinsh;
  face segloop;
  int segindex, facetidx;
  int totalcount = 0;
  int i;

  // both segment-index and facet-index start from zero.
  idx_segment_facet_list = new int[segmentendpointslist_length + 1];
  for (i = 0; i < segmentendpointslist_length + 1; i++) {
    idx_segment_facet_list[i] = 0;
  }

  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != NULL) {
    segindex = getfacetindex(segloop);
    if (idx_segment_facet_list[segindex] == 0) {
      // Count the number of facets at this segment.
      spivot(segloop, startsh);
      spinsh = startsh;
      while (spinsh.sh != NULL) {
        idx_segment_facet_list[segindex]++;
        spivotself(spinsh);
        if (spinsh.sh == startsh.sh) break;
      }
      totalcount += idx_segment_facet_list[segindex];
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // A working list.
  bool *bflags = new bool[segmentendpointslist_length + 1];

  // Have got the totalcount, fill the starting indices into the list.
  int j = idx_segment_facet_list[0], k;
  idx_segment_facet_list[0] = 0;
  //for (i = 0; i < segmentendpointslist_length + 1; i++) {
  for (i = 0; i < segmentendpointslist_length; i++) {
    k = idx_segment_facet_list[i+1];
    idx_segment_facet_list[i+1] = idx_segment_facet_list[i] + j;
    j = k;
    bflags[i] = false;
  }

  segment_facet_list = new int[totalcount + 1];
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != NULL) {
    segindex = getfacetindex(segloop);
    if (!bflags[segindex]) {
      spivot(segloop, startsh);
      spinsh = startsh;
      while (spinsh.sh != NULL) {
        facetidx = getfacetindex(spinsh);
        segment_facet_list[idx_segment_facet_list[segindex]] = facetidx;
        idx_segment_facet_list[segindex]++; // for the next one
        spivotself(spinsh);
        if (spinsh.sh == startsh.sh) break;
      }
      bflags[segindex] = true;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // Counters in idx_segment_facet_list[] are shifted by 1.
  for (i = segmentendpointslist_length - 1; i >= 0; i--) {
    idx_segment_facet_list[i+1] = idx_segment_facet_list[i];
  }
  idx_segment_facet_list[0] = 0;


  delete [] bflags;
}

//============================================================================//
//                                                                            //
// ridge_vertices_adjacent()    Check if two ridge vertices are connected by  //
//                              an input segment.                             //
//                                                                            //
//============================================================================//

int tetgenmesh::ridge_vertices_adjacent(point e1, point e2)
{
  int idx = pointmark(e1);
  int acount = idx_segment_ridge_vertex_list[idx+1]-idx_segment_ridge_vertex_list[idx];
  for (int i = 0; i < acount; i++) {
    if (segment_ridge_vertex_list[idx_segment_ridge_vertex_list[idx]+i] == e2) {
      return 1; // adjacent.
    }
  }
  return 0; // not adjacent.
}

//============================================================================//
//                                                                            //
// facet_ridge_vertex_adjacent()    Check if a facet and a ridge vertex is    //
//                                  adjacent by an input segment.             //
//                                                                            //
//============================================================================//

int tetgenmesh::facet_ridge_vertex_adjacent(face *chkfac, point chkpt)
{
  int ridge_idx = pointmark(chkpt);
  int facet_idx = getfacetindex(*chkfac);
  for (int i = idx_ridge_vertex_facet_list[ridge_idx];
           i < idx_ridge_vertex_facet_list[ridge_idx+1]; i++) {
    if (ridge_vertex_facet_list[i] == facet_idx) {
      return 1; // They are adjacent.
    }
  }
  return 0;
}

//============================================================================//
//                                                                            //
// segsegadjacent()    Check whether two segments, or a segment and a facet,  //
//                     or two facets are adjacent to each other.              //
//                                                                            //
//============================================================================//

int tetgenmesh::segsegadjacent(face *seg1, face *seg2)
{
  int segidx1 = getfacetindex(*seg1);
  int segidx2 = getfacetindex(*seg2);

  if (segidx1 == segidx2) {
    return 2; // Adjacent. They are the same segment.
  }

  point pa1 = segmentendpointslist[segidx1 * 2];
  point pb1 = segmentendpointslist[segidx1 * 2 + 1];
  point pa2 = segmentendpointslist[segidx2 * 2];
  point pb2 = segmentendpointslist[segidx2 * 2 + 1];

  if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) {
    return 1; // Adjacent.
  }
  return 0; // not adjacent
}

//============================================================================//
//                                                                            //
// segfacetadjacent()    Check whether a segment and a facet are adjacent or  //
//                       not.                                                 //
//                                                                            //
//============================================================================//

int tetgenmesh::segfacetadjacent(face *subseg, face *subsh)
{
  int seg_idx   = getfacetindex(*subseg);
  int facet_idx = getfacetindex(*subsh);
  for (int i = idx_segment_facet_list[seg_idx];
           i < idx_segment_facet_list[seg_idx+1]; i++) {
    if (segment_facet_list[i] == facet_idx) {
      return 1; // They are adjacent.
    }
  }
  return 0;
}

//============================================================================//
//                                                                            //
// facetfacetadjacent()    Check whether two facets are adjacent or not.      //
//                                                                            //
//============================================================================//

int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2)
{
  int count = 0, i;

  int fidx1 = getfacetindex(*subsh1);
  int fidx2 = getfacetindex(*subsh2);

  if (fidx1 == fidx2) {
    return 2; // Adjacent. They are the same facet.
  }

  for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) {
    pinfect(facetverticeslist[i]);
  }

  for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2+1]; i++) {
    if (pinfected(facetverticeslist[i])) count++;
  }

  // Uninfect the vertices.
  for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) {
    puninfect(facetverticeslist[i]);
  }

  if (count > 0) {
    return 1;
  } else {
    return 0;
  }
}

//============================================================================//
//                                                                            //
// is_sharp_segment()    Check whether a given segment is sharp or not.       //
//                                                                            //
//============================================================================//

bool tetgenmesh::is_sharp_segment(face *seg)
{
  int segidx = getfacetindex(*seg);
  double mindihedang = segment_info_list[segidx*4];
  return mindihedang < 72.; // (in theory) < 72 degree is sufficient.
}

//============================================================================//
//                                                                            //
// does_seg_contain_acute_vertex()    Check whether one of the endpoints of a //
//                                    given segment is a sharp corner.        //
//                                                                            //
//============================================================================//

bool tetgenmesh::does_seg_contain_acute_vertex(face* seg)
{
  int segidx = getfacetindex(*seg);
  point *ppt = &(segmentendpointslist[segidx * 2]);
  REAL ang = 180.;
  // Get the smallest angle at its endpoints.
  for (int i = 0; i < 2; i++) {
    if ((ppt[i] == sorg(*seg)) || (ppt[i] == sdest(*seg))) {
      if (segment_info_list[segidx * 4 + 2 + i] < ang) {
        ang = segment_info_list[segidx * 4 + 2 + i];
      }
    }
  }
  return ang < 60.;
}

//============================================================================//
//                                                                            //
// create_a_shorter_edge()    Can we create an edge (which is shorter than    //
//                            minedgelength) between the two given vertices?  //
//                                                                            //
//============================================================================//

bool tetgenmesh::create_a_shorter_edge(point steinerpt, point nearpt)
{
  bool createflag = false; // default, do not create a shorter edge.

  enum verttype nearpt_type = pointtype(nearpt);
  enum verttype steiner_type = pointtype(steinerpt);

  if (nearpt_type == RIDGEVERTEX) {
    if (steiner_type == FREESEGVERTEX) {
      // Create a shorter edge if the Steiner point does not on an adjacent
      //   segment of this ridge vertex.
      face parentseg;
      sdecode(point2sh(steinerpt), parentseg);
      int segidx = getfacetindex(parentseg);
      point pa = segmentendpointslist[segidx * 2];
      point pb = segmentendpointslist[segidx * 2 + 1];
      if ((pa != nearpt) && (pb != nearpt)) {
        createflag = true; // create a shorter edge.
      }
    } else if (steiner_type == FREEFACETVERTEX) {
      // Create a shorter edge if the Steiner point does not on an adjacent
      //   facet of this ridge vertex.
      face parentsh;
      sdecode(point2sh(steinerpt), parentsh);
      if (!facet_ridge_vertex_adjacent(&parentsh, nearpt)) {
        createflag = true; // create a shorter edge.
      }
    }
  } else if (nearpt_type == FREESEGVERTEX) {
    if (steiner_type == FREESEGVERTEX) {
      // Check if they are on the same segment.
      face seg1, seg2;
      sdecode(point2sh(steinerpt), seg1);
      sdecode(point2sh(nearpt), seg2);
      int sidx1 = getfacetindex(seg1);
      int sidx2 = getfacetindex(seg2);
      if (sidx1 != sidx2) {
        createflag = true; // create a shorter edge.
      }
    } else if (steiner_type == FREEFACETVERTEX) {
      face parentseg, paresntsh;
      sdecode(point2sh(steinerpt), paresntsh);
      sdecode(point2sh(nearpt), parentseg);
      if (!segfacetadjacent(&parentseg, &paresntsh)) {
        createflag = true; // create a shorter edge.
      }
    }
  } else if (nearpt_type == FREEFACETVERTEX) {
    if (steiner_type == FREESEGVERTEX) {
      //assert(0); // to debug...
      face parentseg, paresntsh;
      sdecode(point2sh(nearpt), paresntsh);
      sdecode(point2sh(steinerpt), parentseg);
      if (!segfacetadjacent(&parentseg, &paresntsh)) {
        createflag = true; // create a shorter edge.
      }
    } else if (steiner_type == FREEFACETVERTEX) {
      // Create a short edge if they are on two different facets.
      face paresntsh1, paresntsh2;
      sdecode(point2sh(nearpt), paresntsh1);
      sdecode(point2sh(steinerpt), paresntsh2);
      int sidx1 = getfacetindex(paresntsh1);
      int sidx2 = getfacetindex(paresntsh2);
      if (sidx1 != sidx2) {
        createflag = true; // create a shorter edge.
      }
    }
  }

  return createflag;
}

//============================================================================//
//                                                                            //
// enqueuesubface()    Queue a subface or a subsegment for encroachment check.//
//                                                                            //
//============================================================================//

void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface)
{
  if (!smarktest2ed(*chkface)) {
    smarktest2(*chkface); // Only queue it once.
    face *queface = (face *) pool->alloc();
    *queface = *chkface;
  }
}

//============================================================================//
//                                                                            //
// 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;
  }
}

//============================================================================//
//                                                                            //
// check_encroachment()    Check whether a given point encroaches upon a line //
//                         segment or not.                                    //
//                                                                            //
// 'checkpt' should not be dummypoint.                                        //
//                                                                            //
//============================================================================//

bool tetgenmesh::check_encroachment(point pa, point pb, point checkpt)
{
  // dot = (pa->checkpt) * (pb->checkpt)
  REAL d = (pa[0] - checkpt[0]) * (pb[0] - checkpt[0])
         + (pa[1] - checkpt[1]) * (pb[1] - checkpt[1])
         + (pa[2] - checkpt[2]) * (pb[2] - checkpt[2]);
  return d < 0.; // cos\theta < 0. ==> 90 < theta <= 180 degree.
}

//============================================================================//
//                                                                            //
// check_enc_segment()    Is a given segment encroached?                      //
//                                                                            //
//============================================================================//

bool tetgenmesh::check_enc_segment(face *chkseg, point *pencpt)
{
  point *ppt = (point *) &(chkseg->sh[3]);

  if (*pencpt != NULL) {
    return check_encroachment(ppt[0], ppt[1], *pencpt);
  }

  triface searchtet, spintet;
  point encpt = NULL, tapex;
  REAL prjpt[3]; // The projection point from encpt to segment.
  REAL minprjdist = 0., prjdist;
  int t1ver;

  sstpivot1(*chkseg, searchtet);
  spintet = searchtet;
  while (1) {
    tapex = apex(spintet);
    if (tapex != dummypoint) {
      if (check_encroachment(ppt[0], ppt[1], tapex)) {
        // Find one encroaching vertex. Calculate its projection distance
        projpt2edge(tapex, ppt[0], ppt[1], prjpt);
        prjdist = distance(tapex, prjpt);
        if (encpt == NULL) {
          encpt = tapex;
          minprjdist = prjdist;
        } else {
          if (prjdist < minprjdist) {
            encpt = tapex;
            minprjdist = prjdist;
          }
        }
      }
    }
    fnextself(spintet);
    if (spintet.tet == searchtet.tet) break;
  }

  if (encpt != NULL) {
    *pencpt = encpt; // Return this enc point.
    return true;
  }

  return false; // do not split it.
}

//============================================================================//
//                                                                            //
// get_steiner_on_segment()    Get the Steiner point to split a given segment.//
//                                                                            //
//============================================================================//

bool tetgenmesh::get_steiner_on_segment(face* seg, point refpt, point steinpt)
{
  point ei = sorg(*seg);
  point ej = sdest(*seg);
  //if (*prefpt == NULL) {
  //  // Check if this segment is encroached by some existing vertices.
  //  assert(0); // to do ...
  //}
  // Is this segment contains an acute seg-seg angle?
  bool acute_flag = false;
  int i;

  if ((refpt) != NULL) {
    // This segment is encroched by an existing vertex.
    REAL L, L1, t;
  
    if (pointtype(refpt) == FREESEGVERTEX) {
      face parentseg;
      sdecode(point2sh(refpt), parentseg);
      int sidx1 = getfacetindex(parentseg);
      point far_pi = segmentendpointslist[sidx1 * 2];
      point far_pj = segmentendpointslist[sidx1 * 2 + 1];
      int sidx2 = getfacetindex(*seg);
      point far_ei = segmentendpointslist[sidx2 * 2];
      point far_ej = segmentendpointslist[sidx2 * 2 + 1];
      if ((far_pi == far_ei) || (far_pj == far_ei)) {
        // Two segments are adjacent at far_ei!
        // Create a Steiner point at the intersection of the segment
        //   [far_ei, far_ej] and the sphere centered at far_ei with 
        //   radius |far_ei - refpt|.
        L = distance(far_ei, far_ej);
        L1 = distance(far_ei, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]);
        }
        REAL lfs_at_steiner = distance(refpt, steinpt);
        //REAL dist_to_ei = distance(steinpt, ei);
        REAL dist_to_ej = distance(steinpt, ej);
        if (/*(dist_to_ei < lfs_at_steiner) ||*/
            (dist_to_ej < lfs_at_steiner)) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
        set_ridge_vertex_protecting_ball(far_ei);
        acute_flag = true;
      } else if ((far_pi == far_ej) || (far_pj == far_ej)) {
        // Two segments are adjacent at far_ej!
        L = distance(far_ei, far_ej);
        L1 = distance(far_ej, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]);
        }
        REAL lfs_at_steiner = distance(refpt, steinpt);
        REAL dist_to_ei = distance(steinpt, ei);
        //REAL dist_to_ej = distance(steinpt, ej);
        if ((dist_to_ei < lfs_at_steiner) /*||
            (dist_to_ej < lfs_at_steiner)*/) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
        set_ridge_vertex_protecting_ball(far_ej);
        acute_flag = true;
      } else {
        // Cut the segment by the projection point of refpt.
        projpt2edge(refpt, ei, ej, steinpt);
        REAL lfs_at_steiner = distance(refpt, steinpt);
        REAL dist_to_ei = distance(steinpt, ei);
        REAL dist_to_ej = distance(steinpt, ej);
        if ((dist_to_ei < lfs_at_steiner) ||
            (dist_to_ej < lfs_at_steiner)) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
      }
    } else if (pointtype(refpt) == RIDGEVERTEX) {
      int sidx2 = getfacetindex(*seg);
      point far_ei = segmentendpointslist[sidx2 * 2];
      point far_ej = segmentendpointslist[sidx2 * 2 + 1];
      if (ridge_vertices_adjacent(far_ei, refpt)) {
        // Thjey are adjacent at far_ei.
        // Create a Steiner point at the intersection of the segment
        //   [far_ei, far_ej] and the sphere centered at far_ei with 
        //   radius |far_ei - refpt|.
        L = distance(far_ei, far_ej);
        L1 = distance(far_ei, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]);
        }
        REAL lfs_at_steiner = distance(refpt, steinpt);
        //REAL dist_to_ei = distance(steinpt, ei);
        REAL dist_to_ej = distance(steinpt, ej);
        if (/*(dist_to_ei < lfs_at_steiner) ||*/
            (dist_to_ej < lfs_at_steiner)) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
        set_ridge_vertex_protecting_ball(far_ei);
        acute_flag = true;
      } else if (ridge_vertices_adjacent(far_ej, refpt)) {
        // Calulate a new point.
        L = distance(far_ei, far_ej);
        L1 = distance(far_ej, refpt);
        t = L1 / L;
        for (i = 0; i < 3; i++) {
          steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]);
        }
        REAL lfs_at_steiner = distance(refpt, steinpt);
        REAL dist_to_ei = distance(steinpt, ei);
        //REAL dist_to_ej = distance(steinpt, ej);
        if ((dist_to_ei < lfs_at_steiner) /*||
            (dist_to_ej < lfs_at_steiner)*/) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
        set_ridge_vertex_protecting_ball(far_ej);
        acute_flag = true;
      } else {
        // Cut the segment by the projection point of refpt.
        projpt2edge(refpt, ei, ej, steinpt);
        REAL lfs_at_steiner = distance(refpt, steinpt);
        REAL dist_to_ei = distance(steinpt, ei);
        REAL dist_to_ej = distance(steinpt, ej);
        if ((dist_to_ei < lfs_at_steiner) ||
            (dist_to_ej < lfs_at_steiner)) {
          // Split the point at the middle.
          for (i = 0; i < 3; i++) {
            steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
          }
        }
       }
    } else if (pointtype(refpt) == FREEFACETVERTEX) {
      // Cut the segment by the projection point of refpt.
      projpt2edge(refpt, ei, ej, steinpt);
      REAL lfs_at_steiner = distance(refpt, steinpt);
      REAL dist_to_ei = distance(steinpt, ei);
      REAL dist_to_ej = distance(steinpt, ej);
      if ((dist_to_ei < lfs_at_steiner) ||
          (dist_to_ej < lfs_at_steiner)) {
        // Split the point at the middle.
        for (i = 0; i < 3; i++) {
          steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
        }
      }
    } else {
      // Cut the segment by the projection point of refpt.
      projpt2edge(refpt, ei, ej, steinpt);
      // Make sure that steinpt is not too close to ei and ej.
      REAL lfs_at_steiner = distance(refpt, steinpt);
      REAL dist_to_ei = distance(steinpt, ei);
      REAL dist_to_ej = distance(steinpt, ej);
      if ((dist_to_ei < lfs_at_steiner) ||
          (dist_to_ej < lfs_at_steiner)) {
        // Split the point at the middle.
        for (i = 0; i < 3; i++) {
          steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
        }
      }
    }

    // Make sure that steinpt is not too close to ei and ej.
  } else {
    // Split the point at the middle.
    for (i = 0; i < 3; i++) {
      steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
    }
  }


  return acute_flag;
}

//============================================================================//
//                                                                            //
// split_segment()    Split a given segment.                                  //
//                                                                            //
// If param != NULL, it contains the circumcenter and its insertion radius    //
// of a bad quality tetrahedron. Tghis circumcenter is rejected since it      //
// encroaches upon this segment.                                              //
//                                                                            //
//============================================================================//

bool tetgenmesh::split_segment(face *splitseg, point encpt, REAL *param,
                               int qflag, int chkencflag, int *iloc)
{
  triface searchtet;
  face searchsh;
  point newpt;
  insertvertexflags ivf;


  insert_point_count++;
  if (!b->quiet && (b->refine_progress_ratio > 0)) {
    if (insert_point_count >= report_refine_progress) {
      printf("  %ld insertions, added %ld points",
             insert_point_count - last_insertion_count,
             points->items - last_point_count);
      last_point_count = points->items; // update it.
      last_insertion_count = insert_point_count;
      if (check_tets_list->objects > 0l) {
        printf(", %ld tetrahedra in queue.\n", check_tets_list->objects);
      } else if (split_subfaces_pool->items > 0l) {
        printf(", %ld subfaces in queue.\n", split_subfaces_pool->items);
      } else {
        printf(", %ld segments in queue.\n", split_segments_pool->items);
      }
      // The next report event
      report_refine_progress *= (1. + b->refine_progress_ratio);
    }
  }
  // Is this segment shared by two facets form an acute dihedral angle?
  int segidx = getfacetindex(*splitseg);
  bool is_sharp = is_sharp_segment(splitseg);

  if (!qflag && (encpt == NULL)) {
    // The split of this segment is due to a rejected ccent of a bad quality
    //   subface or a tetrahedron.
    if (is_sharp) {
      // Do not split a sharp segment.
      *iloc = (int) SHARPCORNER;
      return false;
    }
    // Do not split this segment if one of its endpoints is a sharp corner.
    if (does_seg_contain_acute_vertex(splitseg)) {
      *iloc = (int) SHARPCORNER;
      return false;
    }
  }
  
  // We need to know whether the segment of the new point is adjacent
  //   to another segment which contains the encroached point (encpt).
  makepoint(&newpt, FREESEGVERTEX);
  get_steiner_on_segment(splitseg, encpt, newpt);

  // For create_a_shorter_edge() called in insertpoint().
  setpoint2sh(newpt, sencode(*splitseg));
  
  // Split the segment by the Bowyer-Watson algorithm.
  sstpivot1(*splitseg, searchtet);
  ivf.iloc = (int) ONEDGE;
  ivf.bowywat = 3; // Use Bowyer-Watson, preserve subsegments and subfaces;
  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 (b->metric) {
    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 = b->metric;

  ivf.smlenflag = useinsertradius; // Return the distance to its nearest vertex.
  // Reject a near Steiner point on this segment when:
  //  - it is only encroached by a rejected circumcenter, or
  //  - the insertion of the reject ccent is not due to mesh size (qflag).
  if (!qflag) { //if (!is_adjacent || !qflag) {
    ivf.check_insert_radius = useinsertradius;
  }
  ivf.parentpt = NULL;
  
  if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) {
    st_segref_count++;
    if (steinerleft > 0) steinerleft--;
    if (useinsertradius) {
      REAL rv = 0.0; // param[3]; // emin, maybe zero.
      
      if (is_sharp) {
        // A Steiner point on a sharp segment needs insertion radius.
        // Default use the distance to its neartest vertex.
        double L = ivf.smlen * 0.95; // (ivf.smlen / 3.);
        // Choose the larger one between param[3] and L
        rv = (param[3] > L ? param[3] : L);
        // Record the minimum insertion radius for this segment.
        double minradius = segment_info_list[segidx*4+1];
        if (minradius == 0.) {
          minradius = rv;
        } else {
          if (rv < minradius) minradius = rv;
        }
        segment_info_list[segidx*4+1] = minradius;
      }

      setpointinsradius(newpt, rv); // ivf.smlen
      setpoint2ppt(newpt, ivf.parentpt);
      if (ivf.smlen < smallest_insradius) { // rv?
        smallest_insradius = ivf.smlen;
      }
    }
    if (flipstack != NULL) {
      flipconstraints fc;
      fc.chkencflag = chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      //unflipqueue->restart();
    }
    
    if (later_unflip_queue->objects > b->unflip_queue_limit) {
      recoverdelaunay();
    }
    
    *iloc = ivf.iloc;
    return true;
  } else {
    // Point is not inserted.
    if (ivf.iloc == (int) NEARVERTEX) {
      terminatetetgen(this, 2); // report a bug.
    }


    pointdealloc(newpt);
    
    *iloc = ivf.iloc;
    return false;
  }
}

//============================================================================//
//                                                                            //
// repairencsegs()    Repair encroached (sub) segments.                       //
//                                                                            //
//============================================================================//

void tetgenmesh::repairencsegs(REAL *param, int qflag, int chkencflag)
{
  int split_count = 0, rej_count = 0;
  bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, -D7

  while (ref_segment &&
         ((badsubsegs->items > 0) || (split_segments_pool->items > 0))) {
  
    if (badsubsegs->items > 0) {
      badsubsegs->traversalinit();
      face *bface = (face *) badsubsegs->traverse();
      while (bface != NULL) {
        // A queued segment may have been deleted (split).
        if ((bface->sh != NULL) && (bface->sh[3] != NULL)) {
          // A queued segment may have been processed.
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
            point encpt = NULL;
            if (check_enc_segment(bface, &encpt)) {
              badface *bf = (badface *) split_segments_pool->alloc();
              bf->init();
              bf->ss = *bface;
              bf->forg = sorg(*bface);
              bf->fdest = sdest(*bface);
              bf->noppo = encpt;
              // Push it onto stack.
              bf->nextitem = stack_enc_segments;
              stack_enc_segments = bf;
            }
          }
        }
        bface = (face *) badsubsegs->traverse();
      } // while (bface != NULL)
      badsubsegs->restart();
    } // if (badsubsegs->items > 0)
  
    if (split_segments_pool->items == 0) break;

    // Stop if we have used the desried number of Steiner points.
    if (steinerleft == 0) break;
    // Stop if the desried number of tetrahedra is reached.
    if ((elem_limit > 0) &&
        ((tetrahedrons->items - hullsize) > elem_limit)) break;

    // Pop up an encroached segment.
    badface *bf = stack_enc_segments;
    stack_enc_segments = bf->nextitem;
    if ((bf->ss.sh != NULL) &&
        (sorg(bf->ss) == bf->forg) &&
        (sdest(bf->ss) == bf->fdest)) {
     int iloc = (int) UNKNOWN;
      split_count++;
      if (!split_segment(&(bf->ss), bf->noppo, param, qflag, chkencflag, &iloc)) {
        rej_count++;
      }
    }
    // Return this badface to the pool.
    split_segments_pool->dealloc((void *) bf);
  }

  if (b->verbose > 2) {
    printf("    Trying to split %d segments, %d were rejected.\n",
           split_count, rej_count);
  }

  if (badsubsegs->items > 0) {
    // Clean this list (due to ref_segment).
    badsubsegs->traversalinit();
    face *bface = (face *) badsubsegs->traverse();
    while (bface != NULL) {
      // A queued segment may have been deleted (split).
      if ((bface->sh != NULL) && (bface->sh[3] != NULL)) {
        // A queued segment may have been processed.
        if (smarktest2ed(*bface)) {
          sunmarktest2(*bface);
        }
      }
      bface = (face *) badsubsegs->traverse();
    } // while (bface != NULL)
    badsubsegs->restart();
  } // if (badsubsegs->items > 0)

  if (split_segments_pool->items > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else if (elem_limit > 0) {
      if (b->verbose) {
        printf("The desired number %ld of elements is reached.\n", elem_limit);
      }
    }
    split_segments_pool->restart();
    stack_enc_segments = NULL;
  }
}

//============================================================================//
//                                                                            //
// get_subface_ccent()    Calculate the circumcenter of the diametrical circ- //
//                        umsphere of a given subface.                        //
//                                                                            //
//============================================================================//

bool tetgenmesh::get_subface_ccent(face *chkfac, REAL *pos)
{
  point P = (point) chkfac->sh[3];
  point Q = (point) chkfac->sh[4];
  point R = (point) chkfac->sh[5];

  if (circumsphere(P, Q, R, NULL, pos, NULL)) {
    return true;
  } else {
    terminatetetgen(this, 2);
    return false;
  }

}

//============================================================================//
//                                                                            //
// check_enc_subface()    Check if a given subface is encroached or not.      //
//                                                                            //
//============================================================================//

bool tetgenmesh::check_enc_subface(face *chkfac, point *pencpt, REAL *ccent,
                                   REAL *radius)
{
  triface adjtet;
  point encpt = NULL, pa, pb, pc, toppo;
  REAL prjpt[3], minprjdist = 0., prjdist;
  REAL ori;
  int t1ver;
  
  //get_subface_ccent(chkfac, ccent);
  REAL rd = distance(ccent, sorg(*chkfac));
  *radius = rd;

  if (*pencpt != NULL) {
    // This is only used during the insertion of a Steiner point.
    REAL len = distance(ccent, *pencpt);
    if ((fabs(len - rd) / rd) < 1e-3) len = rd; // Rounding.
    if (len < rd) {
      return true;
    }
    return false;
  }

  stpivot(*chkfac, adjtet);
  if (adjtet.tet == NULL) {
    // This subface is not attached to any tet.
    return false;
  }
  for (int i = 0; i < 2; i++) {
    toppo = oppo(adjtet);
    if (toppo != dummypoint) {
      REAL len = distance(ccent, toppo);
      //if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding.
      if ((fabs(len - rd) / rd) < 1e-3) len = rd; // Rounding.
      if (len < rd) {
        int adjacent = 0; // not adjacent
        if (pointtype(toppo) == RIDGEVERTEX) {
          adjacent = facet_ridge_vertex_adjacent(chkfac, toppo);
        } else if (pointtype(toppo) == FREESEGVERTEX) {
          face parentseg;
          sdecode(point2sh(toppo), parentseg);
          adjacent = segfacetadjacent(&parentseg, chkfac);
        } else if (pointtype(toppo) == FREEFACETVERTEX) {
          face parentsh;
          sdecode(point2sh(toppo), parentsh);
          int facidx1 = getfacetindex(parentsh);
          int facidx2 = getfacetindex(*chkfac);
          if (facidx1 == facidx2) {
            adjacent = 1; // They are on the same facet.
          }
        }
        if (adjacent) {
          // They are adjacent and they are on the same facet.
          flippush(flipstack, &adjtet);
          return false;
        }
        pa = org(adjtet);
        pb = dest(adjtet);
        pc = apex(adjtet);
        projpt2face(toppo, pa, pb, pc, prjpt);
        ori = orient3d(pa, pb, toppo, prjpt);
        if (ori >= 0) {
          ori = orient3d(pb, pc, toppo, prjpt);
          if (ori >= 0) {
            ori = orient3d(pc, pa, toppo, prjpt);
            if (ori >= 0) {
              prjdist = distance(toppo, prjpt);
              if (encpt == NULL) {
                encpt = toppo;
                minprjdist = prjdist;
              } else {
                if (prjdist < minprjdist) {
                  encpt = toppo;
                  minprjdist = prjdist;
                }
              }
            } // if (ori >= 0)
          } // if (ori >= 0)
        } // if (ori >= 0)
      } // if (len < rd)
    }
    fsymself(adjtet);
  }

  if (encpt != NULL) {
    *pencpt = encpt;
    return true;
  }

  return false; // this subface is not encroached.
}

//============================================================================//
//                                                                            //
// check_subface()    Is a given subface in a bad shape (radius-edge ratio)?  //
//                                                                            //
//============================================================================//

bool tetgenmesh::check_subface(face *chkfac, REAL *ccent, REAL radius, REAL *param)
{

  // Get the shortest edge length.
  REAL emin = 1.e+30, dist;
  int shver = 0;
  for (chkfac->shver = 0; chkfac->shver < 3; chkfac->shver++) {
    dist = distance(sorg(*chkfac), sdest(*chkfac));
    if (dist < emin) {
      emin = dist;
      shver = chkfac->shver;
    }
  }
  chkfac->shver = shver;

  REAL ratio = radius / emin;
  if (ratio > b->minratio) {
    // Set a small value to protect this vertex (refer to J. Shewchuk).
    // Enlarge the insertion radius (due to small angle)
    point pa = sorg(*chkfac);
    point pb = sdest(*chkfac);
    REAL ra = getpointinsradius(pa);
    REAL rb = getpointinsradius(pb);
    if (ra > 0.) {
      if (ra > emin) {
        emin = ra;
      }
    }
    if (rb > 0.) {
      if (rb > emin) {
        emin = rb;
      }
    }

    param[3] = emin; // emin / 3.; // (emin * b->minratio);
    param[4] = ratio;
    param[5] = 0.; // not used.
    return true; // need to split it.
  }

  return false;
}

//============================================================================//
//                                                                            //
// enqueue_subface()    Push a badly-shaped subface into the priority queue.  //
//                                                                            //
//============================================================================//

void tetgenmesh::enqueue_subface(face *bface, point encpt, REAL *ccent, REAL *param)
{
  badface *bf = (badface *) split_subfaces_pool->alloc();
  bf->init();
  bf->ss = *bface;
  bf->forg  =  sorg(*bface);
  bf->fdest = sdest(*bface);
  bf->fapex = sapex(*bface);
  bf->noppo = encpt;
  int i;
  for (i = 0; i < 3; i++) bf->cent[i] = ccent[i];
  for (i = 3; i < 6; i++) bf->cent[i] = param[i];

  if (encpt != NULL) {
    // Push it into the encroaching stack.
    bf->nextitem = stack_enc_subfaces;
    stack_enc_subfaces = bf;
  } else {
    // Push it into the priority queue.
    REAL qual = 1.0;
    if (param[4] > 1.) {
      qual = 1.0 / param[4]; // 1 / radius_edge_ratio.
    }
    // Determine the appropriate queue to put the bad subface into.
    int queuenumber = 0;
    if (qual < 1) {
      queuenumber = (int) (64.0 * (1 - qual));
      if (queuenumber > 63) {
        queuenumber = 63;
      }
    } else {
      // It's not a bad shape; put the subface in the lowest-priority queue.
      queuenumber = 0;
    }
    
    // Are we inserting into an empty queue?
    if (queuefront[queuenumber] == (badface *) NULL) {
      // Yes, we are inserting into an empty queue.
      //   Will this become the highest-priority queue?
      if (queuenumber > firstnonemptyq) {
        // Yes, this is the highest-priority queue.
        nextnonemptyq[queuenumber] = firstnonemptyq;
        firstnonemptyq = queuenumber;
      } else {
        // No, this is not the highest-priority queue.
        //   Find the queue with next higher priority.
        int i = queuenumber + 1;
        while (queuefront[i] == (badface *) NULL) {
          i++;
        }
        // Mark the newly nonempty queue as following a higher-priority queue.
        nextnonemptyq[queuenumber] = nextnonemptyq[i];
        nextnonemptyq[i] = queuenumber;
      }
      // Put the bad subface at the beginning of the (empty) queue.
      queuefront[queuenumber] = bf;
    } else {
      // Add the bad tetrahedron to the end of an already nonempty queue.
      queuetail[queuenumber]->nextitem = bf;
    }
    // Maintain a pointer to the last subface of the queue.
    queuetail[queuenumber] = bf;
  }
}

// Return the subface at the front of the queue.
tetgenmesh::badface* tetgenmesh::top_subface()
{
  if (stack_enc_subfaces != NULL) {
    return stack_enc_subfaces;
  } else {
    // Keep a record of which queue was accessed in case dequeuebadtetra()
    //   is called later.
    recentq = firstnonemptyq;
    // If no queues are nonempty, return NULL.
    if (firstnonemptyq < 0) {
      return (badface *) NULL;
    } else {
      // Return the first tetrahedron of the highest-priority queue.
      return queuefront[firstnonemptyq];
    }
  }
}

//============================================================================//
//                                                                            //
// dequeue_subface()    Popup a badly-shaped subface from the priority queue. //
//                                                                            //
//============================================================================//

void tetgenmesh::dequeue_subface()
{
  badface *bf;
  int i;

  if (stack_enc_subfaces != NULL) {
    bf = stack_enc_subfaces;
    stack_enc_subfaces = bf->nextitem;
    // Return the bad subface to the pool.
    split_subfaces_pool->dealloc((void *) bf);
  } else {
    // If queues were empty last time topbadtetra() was called, do nothing.
    if (recentq >= 0) {
      // Find the tetrahedron last returned by topbadtetra().
      bf = queuefront[recentq];
      // Remove the tetrahedron from the queue.
      queuefront[recentq] = bf->nextitem;
      // If this queue is now empty, update the list of nonempty queues.
      if (bf == queuetail[recentq]) {
        // Was this the highest-priority queue?
        if (firstnonemptyq == recentq) {
          // Yes; find the queue with next lower priority.
          firstnonemptyq = nextnonemptyq[firstnonemptyq];
        } else {
          // No; find the queue with next higher priority.
          i = recentq + 1;
          while (queuefront[i] == (badface *) NULL) {
            i++;
          }
          nextnonemptyq[i] = nextnonemptyq[recentq];
        }
      }
      // Return the bad subface to the pool.
      split_subfaces_pool->dealloc((void *) bf);
    }
  }
}

//============================================================================//
//                                                                            //
// parallel_shift()    Parallel shift a triangle along its normal.            //
//                                                                            //
// Given a triangle (a, b, c), create a parallel triangle (pa, pb, pc) at a   //
// distance above (a, b, c).                                                  //
//                                                                            //
//============================================================================//

void tetgenmesh::parallel_shift(point pa, point pb, point pc,
                                point pt, REAL* ppt)
{
  // Get the normal and the average edge length of this triangle.
  REAL N[3], Lav;
  facenormal(pa, pb, pc, N, 1, &Lav);

  // Normalize the normal.
  REAL L = sqrt(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]);
  N[0] /= L;
  N[1] /= L;
  N[2] /= L;
  
  // Calculate the shifted vertices.
  for (int i = 0; i < 3; i++) {
    ppt[0] = pt[0] + Lav * N[0];
    ppt[1] = pt[1] + Lav * N[1];
    ppt[2] = pt[2] + Lav * N[2];
  }

}

//============================================================================//
//                                                                            //
// locate_on_surface()    Locate a vertex in a facet.                         //
//                                                                            //
//============================================================================//

enum tetgenmesh::locateresult
tetgenmesh::locate_on_surface(point searchpt, face* searchsh)
{
  enum locateresult loc = OUTSIDE;

  triface searchtet;
  stpivot(*searchsh, searchtet);
  if (ishulltet(searchtet)) {
    sesymself(*searchsh);
    stpivot(*searchsh, searchtet);
  }

  // Select an edge such that pt lies to CCW of it.
  point pa, pb, pc;
  REAL toppo[3]; // a parallel-shifted point
  REAL n1[3], n2[3], cosang;
  int t1ver; // used by fnextself()
  int i;
  
  for (i = 0; i < 3; i++) {
    pa =  org(searchtet);
    pb = dest(searchtet);
    pc = apex(searchtet);
    parallel_shift(pa, pb, pc, pa, toppo);
    if (orient3d(pa, pb, toppo, searchpt) > 0) {
      break;
    }
    enextself(searchtet);
  }
  if (i == 3) {
    terminatetetgen(this, 2);
  }
  
  while (true) {

    // Let E = [a,b,c] and p lies to the CCW of [a->b].
    // Make sure that the searching vertex and the current subface (a,b,c) are
    //   (nearly) coplanar. We check the dihedral angle between (a,b,c) and
    //   (a,b,searchpt). If it is within the tolerance of co-planar facets,
    //   then we continue the search, otherwise, the search is stopped.
    facenormal(pa, pb, pc, n1, 1, NULL);
    facenormal(pb, pa, searchpt, n2, 1, NULL);
    cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2)));
    if (cosang > cos_facet_separate_ang_tol) {
      // The searching vertex is not coplanar with this subface.
      loc = NONCOPLANAR;
      break;
    }

    parallel_shift(pa, pb, pc, pc, toppo);
    REAL ori1 = orient3d(pb, pc, toppo, searchpt);
    REAL ori2 = orient3d(pc, pa, toppo, searchpt);

    if (ori1 > 0) {
      if (ori2 > 0) {
        //break; // Found.
        loc = ONFACE; break;
      } else if (ori2 < 0) {
        //E.ver = _eprev_tbl[E.ver];
        eprevself(searchtet);
      } else { // ori2 == 0
        //E.ver = _eprev_tbl[E.ver];
        //return LOC_ON_EDGE; // ONEDGE  p lies on edge [c,a]
        eprevself(searchtet);
        loc = ONEDGE; break;
      }
    } else if (ori1 < 0) {
      if (ori2 > 0) {
        //E.ver = _enext_tbl[E.ver];
        enextself(searchtet);
      } else if (ori2 < 0) {
        // Randomly choose one.
        if (rand() % 2) { // flipping a coin.
          //E.ver = _enext_tbl[E.ver];
          enextself(searchtet);
        } else {
          //E.ver = _eprev_tbl[E.ver];
          eprevself(searchtet);
        }
      } else { // ori2 == 0
        //E.ver = _enext_tbl[E.ver];
        enextself(searchtet);
      }
    } else { // ori1 == 0
      if (ori2 > 0) {
        //E.ver = _enext_tbl[E.ver]; // p lies on edge [b,c].
        //return LOC_ON_EDGE; // ONEDGE
        enextself(searchtet);
        loc = ONEDGE; break;
      } else if (ori2 < 0) {
        //E.ver = _eprev_tbl[E.ver];
        eprevself(searchtet);
      } else { // ori2 == 0
        //E.ver = _eprev_tbl[E.ver]; // p is coincident with apex.
        //return LOC_ON_VERT; // ONVERTEX Org(E)
        eprevself(searchtet);
        loc = ONVERTEX; break;
      }
    }

    // Check if we want to cross a segment.
    if (issubseg(searchtet)) {
      loc = ENCSEGMENT; break;
    }

    // Goto the adjacent subface at this subedge.
    int fcount = 0;
    while (fcount < 100000) {
      esymself(searchtet);
      if (issubface(searchtet)) break;
      fsymself(searchtet);
      fcount++;
    }
    if (!issubface(searchtet)) {
      terminatetetgen(this, 2); // report a bug
    }

    // Update the vertices.
    pa =  org(searchtet);
    pb = dest(searchtet);
    pc = apex(searchtet);
    //toppo = oppo(searchtet);
  } // while (true)

  tspivot(searchtet, *searchsh);

  return loc;
}

//============================================================================//
//                                                                            //
// split_subface()    Split a subface.                                        //
//                                                                            //
// param[6], it contains the following data:                                  //
//   [0],[1],[2] - the location of a rejected circumcent,                     //
//   [3] - the samllest edge length ( = insertion radius)                     //
//   [4] - ratio-edge ratio (of this subface).                                //
//         If it is zero, it is an encroached subface.                        //
//   [5] - no used.                                                           //
///                                                                           //
//============================================================================//

bool tetgenmesh::split_subface(face *splitfac, point encpt, REAL *ccent,
                               REAL *param, int qflag, int chkencflag, int *iloc)
{
  triface searchtet;
  face searchsh;
  insertvertexflags ivf;
  point newpt, bak_pts[3], *ppt;
  bool is_adjacent = false;
  bool splitflag = false;  // Indicate if any Steiner point is added.
  int i;

  insert_point_count++;
  if (!b->quiet && (b->refine_progress_ratio > 0.)) {
    if (insert_point_count >= report_refine_progress) {
      printf("  %ld insertions, added %ld points",
             insert_point_count - last_insertion_count,
             points->items - last_point_count);
      last_point_count = points->items; // update it.
      last_insertion_count = insert_point_count;
      if (check_tets_list->objects > 0l) {
        printf(", %ld tetrahedra in queue.\n", check_tets_list->objects);
      } else {
        printf(", %ld subfaces in queue.\n", split_subfaces_pool->items);
      }
      // The next report event
      report_refine_progress *= (1. + b->refine_progress_ratio);
    }
  }

  // Check if this subface is adjacent to a sharp segment, i.e., it is incident
  //   by two facets which form an acute dihedral angle.
  face checkface = *splitfac;
  face checkseg;
  for (i = 0; i < 3; i++) {
    sspivot(checkface, checkseg);
    if (checkseg.sh != NULL) {
      if (is_sharp_segment(&checkseg)) {
        is_adjacent = true;
        break;
      }
    }
    senext2self(checkface);
  }

  if (is_adjacent) {
    // Only split it either it is a bad quality triangle, or due to the
    //   qflag, i.e., mesh size requirement.
    if (!qflag) {
      if (encpt != NULL) {
        *iloc = (int) SHARPCORNER;
        return false; // reject splitting this subface.
      } else {
        if (param[4] == 0.0) {
          // It is not a bad quality subface.
          *iloc = (int) SHARPCORNER;
          return false; // reject splitting this subface.
        }
      }
    }
  } // if (is_adjacent)


  // Deciding the inserting point.
  if (encpt != NULL) {
    // Insert at the projection of the encpt on the facet.
    REAL pos[3];
    ppt = (point *) &(splitfac->sh[3]);
    projpt2face(encpt, ppt[0], ppt[1], ppt[2], pos);
    makepoint(&newpt, FREEFACETVERTEX);
    for (i = 0; i < 3; i++) newpt[i] = pos[i];

    //if (is_adjacent) {
    // Check whether this new position is too close to an existing vertex.
    REAL prjdist = distance(encpt, newpt);
    REAL dist, mindist = 1.e+30;
    for (i = 0; i < 3; i++) {
      dist = distance(ppt[i], newpt);
      if (dist < mindist) mindist = dist;
    }
    if (mindist < prjdist) {
      // Use the circumcenter of this triange instead of the proj of encpt.
      for (i = 0; i < 3; i++) newpt[i] = ccent[i];
    }
    //}
  } else {
    // Split the subface at its circumcenter.
    makepoint(&newpt, FREEFACETVERTEX);
    for (i = 0; i < 3; i++) newpt[i] = ccent[i];
  }

  // This info is needed by create_a_shorter_edge() (called in insertpoint()).
  setpoint2sh(newpt, sencode(*splitfac));


  searchsh = *splitfac;
  ivf.iloc = (int) locate_on_surface(newpt, &searchsh);

  if (ivf.iloc == (int) ENCSEGMENT) {
    // Point lies in the outside of the facet.
    pointdealloc(newpt);
    *iloc = FENSEDIN; // it is a fested in vertex.
    return splitflag;
  } else if (ivf.iloc == (int) ONVERTEX) {
    pointdealloc(newpt);
    *iloc = ONVERTEX;
    return splitflag;
  } else if (ivf.iloc == (int) NONCOPLANAR) {
    pointdealloc(newpt);
    *iloc = NONCOPLANAR;
    return splitflag;
  }

  if ((ivf.iloc != (int) ONFACE) && (ivf.iloc != (int) ONEDGE)) {
    terminatetetgen(this, 2); // report a bug
  }

  // Insert the point.
  stpivot(searchsh, searchtet);
  ivf.bowywat = 3; // Use Bowyer-Watson. Preserve subsegments and subfaces;
  ivf.lawson = 2;
  ivf.rejflag = 1; // Do check the encroachment of segments.
  if (b->metric) {
    ivf.rejflag |= 4;  // Do check encroachment of protecting balls.
  }
  ivf.chkencflag = (chkencflag & (~1));
  ivf.sloc = (int) INSTAR; // ivf.iloc;
  ivf.sbowywat = 3; // ivf.bowywat;
  ivf.splitbdflag = 1;
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric;

  ivf.refineflag = 2;
  ivf.refinesh = *splitfac;

  ivf.smlenflag = useinsertradius; // Update the insertion radius.

  // Reject a near Steiner point on this subface when:
  //  - the insertion of the reject ccent is not due to mesh size (qflag).
  if (!qflag) {
    ivf.check_insert_radius = useinsertradius;
  }
  //if (is_adjacent) {
  //  ivf.parentpt = encpt; // This allows to insert a shorter edge.
  //} else {
    ivf.parentpt = NULL; // init
  //}

  if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) {
    st_facref_count++;
    if (steinerleft > 0) steinerleft--;
    if (useinsertradius) {
      REAL rv = 0.0; // param[3]; // emin, maybe zero.
      
      if (is_adjacent) { // if (encpt != NULL) {
        // A sharp (dihedral) angle is involved.
        // Insertion radius must be > 0.
        double L = (ivf.smlen / 3.);
        // Choose the larger one between param[3] and L
        rv = (param[3] > L ? param[3] : L);
      }

      setpointinsradius(newpt, rv);
      setpoint2ppt(newpt, ivf.parentpt);
      if (smallest_insradius > ivf.smlen) {
        smallest_insradius = ivf.smlen;
      }
    }
    if (flipstack != NULL) {
      flipconstraints fc;
      fc.chkencflag = (chkencflag & (~1)); //chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      //unflipqueue->restart();
    }

    if (later_unflip_queue->objects > b->unflip_queue_limit) {
      recoverdelaunay();
    }
    
    *iloc = ivf.iloc;
    return true;
  }

  // Point is not inserted.
  pointdealloc(newpt);

  if (ivf.iloc == (int) ENCSEGMENT) {
    // Bakup the split subface.
    ppt = (point *) &(splitfac->sh[3]);
    for (i = 0; i < 3; i++) bak_pts[i] = ppt[i];

    bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, or -D7

    if (ref_segment || qflag) {
      // Select an encroached segment and split it.
      for (i = 0; i < encseglist->objects; i++) {
        //face *paryseg = (face *) fastlookup(encseglist, i);
        badface *bf = (badface *) fastlookup(encseglist, i);
        if ((bf->ss.sh == NULL) ||
            (sorg(bf->ss) != bf->forg) ||
            (sdest(bf->ss) != bf->fdest)) continue; // Skip this segment.
        int tmp_iloc;
        if (split_segment(&(bf->ss), NULL, param, qflag, (chkencflag | 1), &tmp_iloc)) {
          // A Steiner point is inserted on an encroached segment.
          // Check if this subface is split as well.
          if ((splitfac->sh == NULL) || (splitfac->sh[3] == NULL)) {
            splitflag = true; break;
          } else {
            ppt = (point *) &(splitfac->sh[3]);
            if ((ppt[0] != bak_pts[0]) ||
                (ppt[1] != bak_pts[1]) ||
                (ppt[2] != bak_pts[2])) {
              splitflag = true; break;
            }
          }
        }
      }
    } // if (ref_segment)
    encseglist->restart();
    // Some segments may be encroached.
    if (badsubsegs->items > 0) {
      //repairencsegs(param, qflag, (chkencflag | 1));
      repairencsegs(param, 0, (chkencflag | 1)); // qflag = 0
    }
    // Check if this subface is split as well.
    if ((splitfac->sh == NULL) || (splitfac->sh[3] == NULL)) {
      splitflag = true;
    } else {
      ppt = (point *) &(splitfac->sh[3]);
      if ((ppt[0] != bak_pts[0]) ||
          (ppt[1] != bak_pts[1]) ||
          (ppt[2] != bak_pts[2])) {
        splitflag = true;
      }
    }
  } else if (ivf.iloc == (int) NEARVERTEX) {
    terminatetetgen(this, 2); // report a bug
  }

  *iloc = ivf.iloc;
  return splitflag;
}

//============================================================================//
//                                                                            //
// repairencfacs()    Repair encroached subfaces.                             //
//                                                                            //
//============================================================================//

void tetgenmesh::repairencfacs(REAL *param, int qflag, int chkencflag)
{
  point encpt = NULL;
  REAL ccent[3], radius; //, param[6] = {0.,};
  int split_count = 0, rej_count = 0;
  //int qflag = 0;
  int i;

  bool ref_subface = ((b->cdtrefine & 2) > 0); // -D2, -D3, -D6, -D7

  // This function may be called from split_tetrahedron(). In this case, the
  //   insertion radius of the rejected circumcenter is stored in param[3].
  //   The check_subface() will return the insertion radius of the circumcenter
  //   of a bad quality subface also in param[3].
  REAL tet_emin = param[3];

  while (ref_subface &&
         ((badsubfacs->items > 0) || (split_subfaces_pool->items > 0))) {

    if (badsubfacs->items > 0) {
      badsubfacs->traversalinit();
      face *bface = (face *) badsubfacs->traverse();
      while (bface != NULL) {
        // A queued subface may have been deleted (split).
        if ((bface->sh != NULL) && (bface->sh[3] != NULL)) {
          // A queued subface may have been processed.
          if (smarktest2ed(*bface)) {
            sunmarktest2(*bface);
            for (i = 3; i < 6; i++) param[i] = 0.; // Clear previous values.
            if (get_subface_ccent(bface, ccent)) {
              encpt = NULL;
              if (check_enc_subface(bface, &encpt, ccent, &radius)) {
                param[3] = tet_emin; // maybe zero.
                enqueue_subface(bface, encpt, ccent, param);
              } else {
                if (check_subface(bface, ccent, radius, param)) {
                  if (tet_emin > 0) {
                    // Use the larger one.
                    param[3] = (param[3] > tet_emin ? param[3] : tet_emin);
                  }
                  enqueue_subface(bface, NULL, ccent, param);
                }
              }
            } else {
              // report a bug.
              terminatetetgen(this, 2);
            }
          }
        }
        bface = (face *) badsubfacs->traverse();
      } // while (bface != NULL)

      badsubfacs->restart(); // clear this pool

      // check_enc_subface() may find some non-Delaunay subfaces.
      if (flippool->items > 0) {
        flipconstraints fc;
        fc.chkencflag = chkencflag;
        fc.enqflag = 2;
        lawsonflip3d(&fc);
      }
    } // if (badsubfacs->items > 0)

    if (split_subfaces_pool->items == 0) break;

    // Stop if we have used the desried number of Steiner points.
    if (steinerleft == 0) break;
    // Stop if the desried number of tetrahedra is reached.
    if ((elem_limit > 0) &&
        ((tetrahedrons->items - hullsize) > elem_limit)) break;


    badface *bf = top_subface();

    if ((bf->ss.sh != NULL) &&
        ( sorg(bf->ss) == bf->forg) &&
        (sdest(bf->ss) == bf->fdest) &&
        (sapex(bf->ss) == bf->fapex)) {
      // Try to split this subface.
      encpt = bf->noppo; // The encroaching vertex.
      for (i = 0; i < 3; i++) ccent[i] = bf->cent[i];
      for (i = 3; i < 6; i++) param[i] = bf->cent[i];
      split_count++;
      
      int iloc = (int) UNKNOWN;
      if (!split_subface(&bf->ss, encpt, ccent, param, qflag, chkencflag, &iloc)) {
        rej_count++;
        if (qflag || ((param[4] > (3. * b->minratio)) && (iloc != SHARPCORNER))) {
          // Queue a unsplit (bad quality) subface.
          badface *bt = NULL;
          unsplit_subfaces->newindex((void **) &bt);
          //bt->init();
          *bt = *bf;
        }
      }
    }
    dequeue_subface();
  } // while ((badsubfacs->items > 0) || (split_subfaces_pool->items > 0))

  if (b->verbose > 3) {
    printf("      Tried to split %d subfaces, %d were rejected.\n",
           split_count, rej_count);
  }
  param[3] = tet_emin; // Restore this value.

  if (badsubfacs->items > 0) {
    // Clean this list (due to the ref_subface flag)
    badsubfacs->traversalinit();
    face *bface = (face *) badsubfacs->traverse();
    while (bface != NULL) {
      // A queued subface may have been deleted (split).
      if ((bface->sh != NULL) && (bface->sh[3] != NULL)) {
        // A queued subface may have been processed.
        if (smarktest2ed(*bface)) {
          sunmarktest2(*bface);
        }
      }
      bface = (face *) badsubfacs->traverse();
    } // while (bface != NULL)
    badsubfacs->restart(); // clear this pool
  } // if (badsubfacs->items > 0)

  if (split_subfaces_pool->items > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else if (elem_limit > 0) {
      if (b->verbose) {
        printf("The desired number %ld of elements is reached.\n", elem_limit);
      }
    }
    split_subfaces_pool->restart(); // Clear this pool.
    unsplit_subfaces->restart();
    stack_enc_subfaces = NULL;
  }
}

//============================================================================//
//                                                                            //
// check_tetrahedron()    Check if the tet needs to be split.                 //
//                                                                            //
// "param[6]" returns the following data:                                     //
//   [0],[1],[2] - the location of the new point                              //
//   [3] - the samllest edge length ( = insertion radius)                     //
//   [4] - the radius-edge ratio                                              //
//   [5] - (optional) edge ratio                                              //
//                                                                            //
// "chktet" returns the shortest edge of this tet.                            //
//                                                                            //
//============================================================================//

bool tetgenmesh::check_tetrahedron(triface *chktet, REAL* param, int &qflag)
{
  point pd = (point) chktet->tet[7];
  if (pd == dummypoint) {
    return false; // Do not split a hull tet.
  }

  point pa = (point) chktet->tet[4];
  point pb = (point) chktet->tet[5];
  point pc = (point) chktet->tet[6];


  REAL D = orient3dexact(pa, pb, pc, pd); // =6*vol

  if (D >= 0.0) {
    // A degenerated tetrahedron.
    terminatetetgen(this, 2);
  }

  qflag = 0; // default

  REAL elen[6];
  REAL vol = -D / 6.0;
  REAL emin = 0., ratio = 0.;

  // Calculate the circumcenter of this tet.
  point P = pa, Q = pb, R = pc, S = pd;

  REAL U[3], V[3], W[3], Z[3]; // variables.
       
  REAL hp = P[0]*P[0] + P[1]*P[1] + P[2]*P[2]; // - wp
  REAL hq = Q[0]*Q[0] + Q[1]*Q[1] + Q[2]*Q[2]; // - wq
  REAL hr = R[0]*R[0] + R[1]*R[1] + R[2]*R[2]; // - wr
  REAL hs = S[0]*S[0] + S[1]*S[1] + S[2]*S[2]; // - wr

  U[0] = hp; U[1] = P[1]; U[2] = P[2];
  V[0] = hq; V[1] = Q[1]; V[2] = Q[2];
  W[0] = hr; W[1] = R[1]; W[2] = R[2];
  Z[0] = hs; Z[1] = S[1]; Z[2] = S[2];

  REAL D1 = orient3d(U, V, W, Z);
        
  U[0] = P[0]; U[1] = hp; //U[2] = P[2];
  V[0] = Q[0]; V[1] = hq; //V[2] = Q[2];
  W[0] = R[0]; W[1] = hr; //W[2] = R[2];
  Z[0] = S[0]; Z[1] = hs; //Z[2] = S[2];

  REAL D2 = orient3d(U, V, W, Z);

  /*U[0] = P[0];*/ U[1] = P[1]; U[2] = hp;
  /*V[0] = Q[0];*/ V[1] = Q[1]; V[2] = hq;
  /*W[0] = R[0];*/ W[1] = R[1]; W[2] = hr;
  /*Z[0] = S[0];*/ Z[1] = S[1]; Z[2] = hs;

  REAL D3 = orient3d(U, V, W, Z);

  REAL DD = D * 2.;

  param[0] = D1 / DD;
  param[1] = D2 / DD;
  param[2] = D3 / DD;


  param[4] = 1.0; // default a good ratio.
  param[5] = vol;

  elen[0] = distance2(pc, pd);
  elen[1] = distance2(pd, pa);
  elen[2] = distance2(pa, pb);
  elen[3] = distance2(pb, pc);
  elen[4] = distance2(pb, pd);
  elen[5] = distance2(pa, pc);

  // Find the shortest edge.
  emin = elen[0];
  int eidx = 0;
  for (int i = 1; i < 6; i++) {
    if (emin > elen[i]) {
      emin = elen[i]; eidx = i;
    }
  }
  emin = sqrt(emin);
  // Let chktet be the shortest edge in this tet.
  chktet->ver = edge2ver[eidx];

  // check mesh size (qflag).
  if (b->varvolume || b->fixedvolume) { // -a#
    if (b->fixedvolume) {
      if (vol > b->maxvolume) {
        // set the insertion radius, use the smaller one between the
        //   smallest edge length of this tet and mesh size;
        emin = (emin < b->maxvolume_length ? emin : b->maxvolume_length);
        qflag = 1;
      }
    }
    if (!qflag && b->varvolume) {
      REAL volbnd = volumebound(chktet->tet);
      if ((volbnd > 0.0) && (vol > volbnd)) {
        // set the insertion radius;
        REAL msize = pow(volbnd, 1./3.) / 3.;
        emin = (emin < msize ? emin : msize);
        qflag = 1;
      }
    }
  } // -a#

  if (!qflag && b->metric) { // -m
    //int eidx = 0;
    for (int i = 0; i < 6; i++) {
      elen[i] = sqrt(elen[i]);
    }
    if (pa[pointmtrindex] > 0) {
      // Get the longest edge {pa, pd}, {pa, pb}, {pa, pc}
      REAL maxelen = elen[1]; //eidx = 1;
      if (maxelen < elen[2]) {maxelen = elen[2]; /*eidx = 2;*/}
      if (maxelen < elen[5]) {maxelen = elen[5]; /*eidx = 5;*/}
      maxelen /= 2.0;
      if (maxelen > pa[pointmtrindex]) {
        emin = (emin < pa[pointmtrindex] ? emin : pa[pointmtrindex]);
        //emax = maxelen;
        qflag = 1;
      }
    }
    if (!qflag && (pb[pointmtrindex] > 0)) {
      // Get the longest edge at pb.
      REAL maxelen = elen[2]; //eidx = 2;
      if (maxelen < elen[3]) {maxelen = elen[3]; /*eidx = 3;*/}
      if (maxelen < elen[4]) {maxelen = elen[4]; /*eidx = 4;*/}
      maxelen /= 2.0;
      if (maxelen > pb[pointmtrindex]) {
        emin = (emin < pb[pointmtrindex] ? emin : pb[pointmtrindex]);
        //emax = maxelen;
        qflag = 1;
      }
    }
    if (!qflag && (pc[pointmtrindex] > 0)) {
      // Get the longest edge at pc.
      REAL maxelen = elen[0]; //eidx = 0;
      if (maxelen < elen[3]) {maxelen = elen[3]; /*eidx = 3;*/}
      if (maxelen < elen[5]) {maxelen = elen[5]; /*eidx = 5;*/}
      maxelen /= 2.0;
      if (maxelen > pc[pointmtrindex]) {
        emin = (emin < pc[pointmtrindex] ? emin : pc[pointmtrindex]);
        //emax = maxelen;
        qflag = 1;
      }
    }
    if (!qflag && (pd[pointmtrindex] > 0)) {
      // Get the longest edge at pd.
      REAL maxelen = elen[0]; //eidx = 0;
      if (maxelen < elen[1]) {maxelen = elen[1]; /*eidx = 1;*/}
      if (maxelen < elen[4]) {maxelen = elen[4]; /*eidx = 4;*/}
      maxelen /= 2.0;
      if (maxelen > pd[pointmtrindex]) {
        emin = (emin < pd[pointmtrindex] ? emin : pd[pointmtrindex]);
        //emax = maxelen;
        qflag = 1;
      }
    }
  } // if (!qflag && b->metric) // -m

  if (qflag) {
    param[3] = emin; // The desired mesh size.
    //param[4] = 1.0; // ratio; // = 0.
    //param[5] = vol;
    return true;
  }

  if (b->minratio > 1.0) {
    REAL radius = distance(param, pa);

    ratio = radius / emin;


    if (ratio > b->minratio) {
      //qflag = 0;
      // The smallest insertion radius should be at least larger than
      //   the smallest edge length (==> graded mesh size).
      point pa = org(*chktet);
      point pb = dest(*chktet);
      REAL ra = getpointinsradius(pa);
      REAL rb = getpointinsradius(pb);
      if ((ra > 0.) && (ra > emin)) {
        emin = ra; // the relaxed (enlarged) insertion radius.
      }
      if ((rb > 0.) && (rb > emin)) {
        emin = rb; // the relaxed (enlarged) insertion radius.
      }

      param[3] = emin; // (emin * b->minratio);
      param[4] = ratio;
      //param[5] = vol;
      return true;
    }
  }

  return false; // no need to split this tetrahedron.
}

//============================================================================//
//                                                                            //
// checktet4split()    Check if a given tet has a bad shape.                  //
//                                                                            //
//============================================================================//

bool tetgenmesh::checktet4split(triface *chktet, REAL* param, int& qflag)
{
  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, rd, Lmax, Lmin;
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i, j;

  if (b->convex) { // -c
    // Skip this tet if it lies in the exterior.
    if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) {
    return 0;
    }
  }

  qflag = 0;
  for (i = 0; i < 6; i++) param[i] = 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)) {
    // Is it a degenerated tet (vol = 0).
    REAL D = orient3dexact(pa, pb, pc, pd); // =6*vol
    if (D >= 0.0) {
      // A degenerated tetrahedron.
      terminatetetgen(this, 2);
    }
    // We temporarily leave this tet. It should be fixed by mesh improvement.
    return false;
  }

  // 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++) param[i] = pd[i] + rhs[i];
  rd = sqrt(dot(rhs, rhs));

  // 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) {
      return true;
    }
  }

  if (b->metric) { // -m option. Check mesh size.
    // 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 true;
        }
      }
    }
  }

  if (in->tetunsuitable != NULL) {
    // Execute the user-defined meshing sizing evaluation.
    if ((*(in->tetunsuitable))(pa, pb, pc, pd, NULL, 0)) {
      return true;
    }
  }


  // Check the radius-edge ratio. Set by -q#.
  if (b->minratio > 0) {
    elen[0] = dot(vdc, vdc);
    elen[1] = dot(vda, vda);
    elen[2] = dot(vab, vab);
    elen[3] = dot(vbc, vbc);
    elen[4] = dot(vdb, vdb);
    elen[5] = dot(vca, vca);

    Lmax = Lmin = elen[0];
    int eidx = 0;
    for (i = 1; i < 6; i++) {
      Lmax = (Lmax < elen[i] ? elen[i] : Lmax);
      //Lmin = (Lmin > elen[i] ? elen[i] : Lmin);
      if (Lmin > elen[i]) {
        Lmin = elen[i]; eidx = i;
      }
    }
    // Let chktet be the shortest edge in this tet.
    chktet->ver = edge2ver[eidx];

    //Lmax = sqrt(Lmax);
    Lmin = sqrt(Lmin);
    D = rd / Lmin;
    if (D > b->minratio) {
      // A bad radius-edge ratio.
      param[3] = Lmin;
      param[4] = D;
      param[5] = sqrt(Lmax) / Lmin; // edge ratio.
      return true;
    }
  } // if (b->minratio > 0)

  // Check the minimum dihedral angle. Set by -q/#.
  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]));
      if (L[i] == 0) {
        terminatetetgen(this, 2);
      }
      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 dihedral 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) {
      // A bad dihedral angle.
      return true;
    }
  } // if (b->mindihedral > 0)

  return 0;
}

//============================================================================//
//                                                                            //
// locate_point_walk()    Locate a point by line searching.                   //
//                                                                            //
//============================================================================//

enum tetgenmesh::locateresult
  tetgenmesh::locate_point_walk(point searchpt, triface* searchtet, int chkencflag)
{
  // Construct the starting point to be the barycenter of 'searchtet'.
  REAL startpt[3];
  point *ppt = (point *) &(searchtet->tet[4]);
  for (int i = 0; i < 3; i++) {
    startpt[i] = (ppt[0][i] + ppt[1][i] + ppt[2][i] + ppt[3][i]) / 4.;
  }

  point torg, tdest, tapex, toppo;
  REAL ori, oriorg, oridest, oriapex;
  enum locateresult loc = OUTSIDE;
  enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;

  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) break;
  }

  if (searchtet->ver == 4) {
    terminatetetgen(this, 2);
  }
  int max_visited_tets = 10000; // tetrahedrons->items;

  // Walk through tetrahedra to locate the point.
  while (max_visited_tets > 0) {
    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 the crruent face of `serarchtet', which face do we exit?
    // Find the next face which is intersect with the line (startpt->searchpt).
    oriorg  = orient3d(tdest, tapex, toppo, searchpt);
    oridest = orient3d(tapex,  torg, toppo, searchpt);
    oriapex = orient3d( torg, tdest, toppo, searchpt);

    if (oriorg < 0) {
      if (oridest < 0) {
        if (oriapex < 0) {
          // All three faces are possible.
          if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = ORGMOVE;
          } else if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = DESTMOVE;
          } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = APEXMOVE;
          } else {
            int 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.
          if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = ORGMOVE;
          } else if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = DESTMOVE;
          } else {
            //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.
          if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = ORGMOVE;
          } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = APEXMOVE;
          } else {
            //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.
          if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = DESTMOVE;
          } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) {
            nextmove = APEXMOVE;
          } else {
            //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;
    //}
    decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself
    if (ishulltet(*searchtet)) {
      loc = OUTSIDE; // return OUTSIDE;
      break;
    }
    max_visited_tets--;

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

  return loc;
}

//============================================================================//
//                                                                            //
// splittetrahedron()    Split a tetrahedron.                                 //
//                                                                            //
//============================================================================//

bool tetgenmesh::split_tetrahedron(triface* splittet, // the tet to be split.
  REAL *param, // param[6], it contains the following data
               // [0],[1],[2] - the location of the new point
               // [3] - the samllest edge length ( = insertion radius)
               // [4] - radius-edge ratio
               // [5] - its volume
  int qflag,   // split due to mesh size enforcement.
  int chkencflag,
  insertvertexflags &ivf)
{
  triface searchtet;
  point newpt, bak_pts[4], *ppt;
  bool splitflag = false;
  int i;


  insert_point_count++;
  if (!b->quiet && (b->refine_progress_ratio > 0.)) {
    if (insert_point_count >= report_refine_progress) {
      printf("  %ld insertions, added %ld points, %ld tetrahedra in queue.\n",
             insert_point_count - last_insertion_count,
             points->items - last_point_count,
             check_tets_list->objects);
      last_point_count = points->items; // update it.
      last_insertion_count = insert_point_count;
      // The next report event
      report_refine_progress *= (1. + b->refine_progress_ratio);
    }
  }

  makepoint(&newpt, FREEVOLVERTEX);
  for (i = 0; i < 3; i++) newpt[i] = param[i];

  // Locate the new point. Starting from an interior point 'q' of the
  //   splittet. We perform a walk from q to the 'newpt', stop walking
  //   either we hit a subface or enter OUTSIDE.
  searchtet = *splittet;
  ivf.iloc = (int) OUTSIDE;
  //ivf.iloc = locate(newpt, &searchtet, 1); // 'chkencflag' = 1.
  ivf.iloc = locate_point_walk(newpt, &searchtet, 1); // 'chkencflag' = 1.


  if ((ivf.iloc == (int) ENCSUBFACE) || (ivf.iloc == (int) OUTSIDE)) {
    // The circumcenter 'c' is not visible from 'q' (the interior of the tet).
    pointdealloc(newpt);  // Do not insert this vertex.


    ivf.iloc = (int) FENSEDIN;
    return splitflag;
  } // if (ivf.iloc == (int) ENCSUBFACE)

  // 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 (b->metric) {
    ivf.rejflag |= 4; // Reject it if it lies in some protecting balls.
  }
  ivf.chkencflag = (chkencflag & (~3)); // chkencflag;
  ivf.sloc = ivf.sbowywat = 0; // No use.
  ivf.splitbdflag = 0; // No use (its an interior vertex).
  ivf.validflag = 1;
  ivf.respectbdflag = 1;
  ivf.assignmeshsize = b->metric;

  // Mesh refinement options.
  ivf.refineflag = 1;
  ivf.refinetet = *splittet;
  // get the shortest edge length to the new point.
  ivf.smlenflag = useinsertradius;
  if (!qflag) {
    // Avoid creating an unnecessarily short edge.
    ivf.check_insert_radius = useinsertradius;
  } else {
    ivf.check_insert_radius = 0;
  }
  ivf.parentpt = NULL; // init.

  if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) {
    // Vertex is inserted.
    st_volref_count++;
    if (steinerleft > 0) steinerleft--;
    if (useinsertradius) {
      // Save the shortest edge between: emin and ivf.smlen
      REAL rv = 0.0; // ivf.smlen;
      if (param[3] > 0.0) { // The smallest edge length of this tet.
        rv = (param[3] < ivf.smlen ? param[3] : ivf.smlen);
      }
      setpointinsradius(newpt, rv); // ivf.smlen
      setpoint2ppt(newpt, ivf.parentpt);
      if (ivf.smlen < smallest_insradius) { // ivf.smlen
        smallest_insradius = ivf.smlen;
      }
    }
    if (flipstack != NULL) {                 
      flipconstraints fc;
      fc.chkencflag = (chkencflag & (~3)); //chkencflag;
      fc.enqflag = 2;
      lawsonflip3d(&fc);
      //unflipqueue->restart();
    }

    if (later_unflip_queue->objects > b->unflip_queue_limit) {
      recoverdelaunay();
    }

    return true;
  } 

  // Point is not inserted.
  pointdealloc(newpt);

  if (ivf.iloc == (int) ENCSEGMENT) {
    if (!b->nobisect) { //if (!b->nobisect && qflag) { // no -Y
      // bakup the vertices of this tet.
      ppt = (point *) &(splittet->tet[4]);
      for (i = 0; i < 4; i++) bak_pts[i] = ppt[i];
    
      bool ref_segment = ((b->cdtrefine & 1) > 0);
    
      if (ref_segment || qflag) {
        for (i = 0; i < encseglist->objects; i++) {
          //face *paryseg = (face *) fastlookup(encseglist, i);
          badface *bf = (badface *) fastlookup(encseglist, i);
          if ((bf->ss.sh == NULL) ||
              (sorg(bf->ss) != bf->forg) ||
              (sdest(bf->ss) != bf->fdest)) {
            continue; // Skip this segment.
          }
          int tmp_iloc;
          if (split_segment(&(bf->ss), NULL, param, qflag, (chkencflag | 3), &tmp_iloc)) {
            // A Steienr point is inserted on a segment.
            // Check if this tet is split as well.
            if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) {
              splitflag = true; // The tet is split as well.
            } else {
              ppt = (point *) &(splittet->tet[4]);
              if ((ppt[0] != bak_pts[0]) ||
                  (ppt[1] != bak_pts[1]) ||
                  (ppt[2] != bak_pts[2]) ||
                  (ppt[3] != bak_pts[3])) {
                splitflag = true; // The tet is split as well.
              }
            }
            if (splitflag) {
              break; // This tetrahedron is split.
            }
          }
        } // i
      } // if (ref_segment ||qflag)
      encseglist->restart();
      // Some segments may need to be repaired.
      if (badsubsegs->items > 0) {
        //repairencsegs(param, qflag, (chkencflag | 3)); // Queue new enroached subsegments and subfaces.
        repairencsegs(param, 0, (chkencflag | 3)); // qflag = 0
      }
      // Some subfaces may need to be repaired.
      if (badsubfacs->items > 0) {
        //repairencfacs(param, qflag, (chkencflag | 2)); // Queue new encroached subfaces.
        repairencfacs(param, 0, (chkencflag | 2)); // qflag = 0
        if (unsplit_subfaces->objects > 0) {
          unsplit_subfaces->restart(); // clear this list;
        }
      }
      // Check if this tet is split as well.
      if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) {
        splitflag = true; // The tet is split as well.
      } else {
        ppt = (point *) &(splittet->tet[4]);
        if ((ppt[0] != bak_pts[0]) ||
            (ppt[1] != bak_pts[1]) ||
            (ppt[2] != bak_pts[2]) ||
            (ppt[3] != bak_pts[3])) {
          splitflag = true; // The tet is split as well.
        }
      }
    } else { // if (!b->nobisect) { // no -Y
      encseglist->restart();
    }
  } else if (ivf.iloc == (int) ENCSUBFACE) {
    if (!b->nobisect) { //if (!b->nobisect && qflag) { // no -Y
      // bakup the vertices of this tet.
      ppt = (point *) &(splittet->tet[4]);
      for (i = 0; i < 4; i++) bak_pts[i] = ppt[i];
    
      bool ref_subface = ((b->cdtrefine & 2) > 0);
    
      if (ref_subface || qflag) {
        // This rejected Steiner point may encroach upon more than one subfaces.
        //   We split the one which contains the projection of this rejected
        //   Steiner point. Moreover, there may be many subfaces.
        triface adjtet;
        point pa, pb, pc, toppo;
        REAL prjpt[3], ori;
        int scount = 0;
        int t1ver;
        
        // Clean the bad radius-edge ratio, so split_subface() knows that
        //   the split of this subface is due to a rejected tet ccenter.
        param[4] = 0.0;
    
        for (i = 0; i < encshlist->objects; i++) {
          badface *bface = (badface *) fastlookup(encshlist, i);
          // This subface may be split.
          if ((bface->ss.sh == NULL) ||
              (sorg(bface->ss) != bface->forg) ||
              (sdest(bface->ss) != bface->fdest) ||
              (sapex(bface->ss) != bface->fapex)) {
            continue;
          }
          stpivot(bface->ss, adjtet);
          if (ishulltet(adjtet)) {
            fsymself(adjtet);
          }
          toppo = oppo(adjtet); // used by orient3d()
          //assert(toppo != dummypoint);
          pa = org(adjtet);
          pb = dest(adjtet);
          pc = apex(adjtet);
          projpt2face(param, pa, pb, pc, prjpt);
          ori = orient3d(pa, pb, toppo, prjpt);
          if (ori >= 0) {
            ori = orient3d(pb, pc, toppo, prjpt);
            if (ori >= 0) {
              ori = orient3d(pc, pa, toppo, prjpt);
              if (ori >= 0) {
                scount++;
                // Found such a subface, try to split it.
                int tmp_iloc;
                split_subface(&(bface->ss), NULL, bface->cent, param, qflag,
                              chkencflag | 2, &tmp_iloc);
                // This subface may not be split while some encroached subsegments
                //   might be split.
                // Check if this tet is split as well.
                if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) {
                  splitflag = true; // The tet is split as well.
                } else {
                  ppt = (point *) &(splittet->tet[4]);
                  if ((ppt[0] != bak_pts[0]) ||
                      (ppt[1] != bak_pts[1]) ||
                      (ppt[2] != bak_pts[2]) ||
                      (ppt[3] != bak_pts[3])) {
                    splitflag = true; // The tet is split as well.
                  }
                }
                if (splitflag) {
                  break;
                }
              } // if (ori >= 0)
            }
          }
        } // i
        if (scount == 0) {
          // Not such subface is found! This can happen due to the existence
          //   of small angles and non-Delaunay elements.
          // Select an encroached subface and split it.
          for (i = 0; i < encshlist->objects; i++) {
            badface *bface = (badface *) fastlookup(encshlist, i);
            if ((bface->ss.sh == NULL) ||
                (sorg(bface->ss) != bface->forg) ||
                (sdest(bface->ss) != bface->fdest) ||
                (sapex(bface->ss) != bface->fapex)) {
              continue;
            }
            //if (get_subface_ccent(&(bface->ss), ccent)) {
            int tmp_iloc;
            split_subface(&(bface->ss), NULL, bface->cent, param, qflag,
                          chkencflag | 2, &tmp_iloc);
            // Check if this tet is split as well.
            if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) {
              splitflag = true; // The tet is split as well.
            } else {
              ppt = (point *) &(splittet->tet[4]);
              if ((ppt[0] != bak_pts[0]) ||
                  (ppt[1] != bak_pts[1]) ||
                  (ppt[2] != bak_pts[2]) ||
                  (ppt[3] != bak_pts[3])) {
                splitflag = true; // The tet is split as well.
              }
            }
            if (splitflag) {
              break; // This tetrahedron is split.
            }
          }
        } // if (scount == 0)
      } // if (ref_subface)
      encshlist->restart(); // Clear the list.
      // Some subfaces may need to be repaired.
      if (badsubfacs->items > 0) {
        //repairencfacs(param, qflag, (chkencflag | 2)); // Queue new encroached subfaces.
        repairencfacs(param, 0, (chkencflag | 2)); // qflag = 0
        if (unsplit_subfaces->objects > 0) {
          unsplit_subfaces->restart(); // clear this list.
        }
      }
      // Check if this tet is split as well.
      if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) {
        splitflag = true; // The tet is split as well.
      } else {
        ppt = (point *) &(splittet->tet[4]);
        if ((ppt[0] != bak_pts[0]) ||
            (ppt[1] != bak_pts[1]) ||
            (ppt[2] != bak_pts[2]) ||
            (ppt[3] != bak_pts[3])) {
          splitflag = true; // The tet is split as well.
        }
      }
    } else { // if (!b->nobisect)
      encshlist->restart();
    }
  }

  return splitflag;
}

//============================================================================//
//                                                                            //
// repairbadtets()    Repair bad quality tetrahedra.                          //
//                                                                            //
//============================================================================//

void tetgenmesh::repairbadtets(REAL queratio, int chkencflag)
{
  triface *bface, *quetet, *last_quetet;
  triface checktet;
  REAL param[6] = {0.,};
  int qflag = 0;
  int i;

  while ((badtetrahedrons->items > 0) || (check_tets_list->objects > 0)) {

    if (badtetrahedrons->items > 0) {
      badtetrahedrons->traversalinit();
      bface = (triface *) badtetrahedrons->traverse();
      while (bface != NULL) {
        check_tets_list->newindex((void **) &quetet);
        *quetet = *bface;
        bface = (triface *) badtetrahedrons->traverse();
      }
      badtetrahedrons->restart();
    }
    
    // Stop if we have used the desried number of Steiner points.
    if (steinerleft == 0) break;
    // Stop if the desried number of tetrahedra is reached.
    if ((elem_limit > 0) &&
        ((tetrahedrons->items - hullsize) > elem_limit)) break;


    // Randomly select a tet to split.
    i = rand() % check_tets_list->objects;
    quetet = (triface *) fastlookup(check_tets_list, i);
    checktet = *quetet;
    
    // Fill the current position by the last tet in the list.
    i = check_tets_list->objects - 1;
    last_quetet = (triface *) fastlookup(check_tets_list, i);
    *quetet = *last_quetet;
    check_tets_list->objects--;

    if (!isdeadtet(checktet)) {
      if (marktest2ed(checktet)) {
        unmarktest2(checktet);
        //if (check_tetrahedron(&checktet, param, qflag)) {
        if (checktet4split(&checktet, param, qflag)) {
          bool splitflag = false;
          insertvertexflags ivf;
          splitflag = split_tetrahedron(&checktet, param, qflag, chkencflag, ivf);
          if (!splitflag) {
            if (qflag || (param[4] > queratio)) { // radius-edge ratio
              badface *bt = NULL;
              unsplit_badtets->newindex((void **) &bt);
              bt->init();
              bt->tt = checktet;
              bt->forg  =  org(checktet);
              bt->fdest = dest(checktet);
              bt->fapex = apex(checktet);
              bt->foppo = oppo(checktet);
              for (i = 0; i < 6; i++) bt->cent[i] = param[i];
              bt->key = (double) qflag;
            }
          }
        }
      }
    } // if (!isdeadtet(checktet)) {

  } // while ((badtetrahedrons->items > 0) || (check_tets_list->objects > 0))

  if (check_tets_list->objects > 0) {
    if (steinerleft == 0) {
      if (b->verbose) {
        printf("The desired number of Steiner points is reached.\n");
      }
    } else if (elem_limit > 0) {
      if (b->verbose) {
        printf("The desired number %ld of elements is reached.\n", elem_limit);
      }
    }
    //split_tets_pool->restart(); // Clear this pool.
    // Unmark all unchecked tetrahedra.
    for (i = 0; i < check_tets_list->objects; i++) {
      quetet = (triface *) fastlookup(check_tets_list, i);
      if (!isdeadtet(*quetet)) {
        unmarktest2(*quetet);
      }
    }
    check_tets_list->restart();
  }
}

//============================================================================//
//                                                                            //
// delaunayrefinement()    Refine the mesh by Delaunay refinement.            //
//                                                                            //
//============================================================================//

void tetgenmesh::delaunayrefinement()
{
  triface checktet;
  face checksh;
  face checkseg;
  long steinercount;
  REAL param[6] = {0., 0., 0., 0., 0., 0.};
  int qflag = 0;
  int chkencflag = 0;
  int i;

  long bak_segref_count, bak_facref_count, bak_volref_count;

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

  if (b->verbose) {
    printf("  Min radius-edge ratio = %g.\n", b->minratio);
    if (b->mindihedral > 0.) {
      printf("  Min dihedral   angle  = %g.\n", b->mindihedral);
    }
    if (b->fixedvolume) {
      printf("  Max tet volume  = %g.\n", b->maxvolume);
    }
    //printf("  Min Edge length = %g.\n", b->minedgelength);
  }
  // Used in locate_point_on_surface();
  cos_facet_separate_ang_tol = cos(b->facet_separate_ang_tol/180.*PI); // -p/#
  // Used in function is_collinear_at(mid, left, right);
  cos_collinear_ang_tol = cos(b->collinear_ang_tol/180.*PI); // -p///#

  // The cosine value of the min dihedral angle (-q/#) for tetrahedra.
  cosmindihed = cos(b->mindihedral / 180.0 * PI);

  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->elem_growth_ratio > 0.0)) { // -r#
    int ntet = in->numberoftetrahedra; // tetrahedrons->items - hullsize;
    elem_limit = ntet * (1.0 + b->elem_growth_ratio);
  }

  if (b->refine_progress_ratio > 0) { // -r/# default is 0.333
    insert_point_count = 0l;
    last_insertion_count = 0l;
    last_point_count = points->items;
    report_refine_progress = points->items * (1. + b->refine_progress_ratio);
  }

  if (!b->nobisect) { // no -Y.
    if (segmentendpointslist == NULL) {
      makesegmentendpointsmap(); // create ridge_vertex-to-segment map.
    }
    create_segment_info_list();
    makefacetverticesmap();      // create ridge_vertex-to-facet map.
    create_segment_facet_map();  // vreate segment-to-facet map.
  }


  // Begin of memory allocation ===============================================
  // Initialize the pools and priority queues.
  long bls = b->shellfaceperblock;
  long blt = b->tetrahedraperblock;

  badsubsegs = new memorypool(sizeof(face), 256, sizeof(void *), 0);
  badsubfacs = new memorypool(sizeof(face), 256, sizeof(void *), 0);
  badtetrahedrons = new memorypool(sizeof(triface), blt, sizeof(void *), 0);
  
  split_segments_pool = new memorypool(sizeof(badface), bls, sizeof(void *), 0);
  split_subfaces_pool = new memorypool(sizeof(badface), bls, sizeof(void *), 0);

  long est_size = blt;
  int log2objperblk = 0;
  while (est_size >>= 1) log2objperblk++;
  if (log2objperblk < 10) log2objperblk = 10; // At least 1024.

  check_tets_list = new arraypool(sizeof(triface), log2objperblk);
  
  unsplit_segments = new arraypool(sizeof(badface), 10);
  unsplit_subfaces = new arraypool(sizeof(badface), 10);
  unsplit_badtets = new arraypool(sizeof(badface), 10);
  
  stack_enc_segments = stack_enc_subfaces = NULL;

  for (i = 0; i < 64; i++) {
    queuefront[i] = NULL;
  }
  firstnonemptyq = -1;
  recentq = -1;

  encseglist = new arraypool(sizeof(badface), 8);
  encshlist  = new arraypool(sizeof(badface), 8);
  // End of memory allocation =================================================


  // with -r and an .elem file ================================================
  if (b->refine && (in->refine_elem_list != NULL)) {
    if (b->verbose) {
      printf("  Refining a list of given elements.\n");
    }
    //assert(b->varvolume > 0); // -a option must be used.
    chkencflag = 4; // Check bad tetrahedra.
    steinercount = points->items;

    REAL queratio = b->minratio > 2. ? b->minratio : 2.0;
    queratio *= 2.0; // queratio; // increase this value.

    // Create a map from indices to points.
    point *idx2verlist;
    makeindex2pointmap(idx2verlist);

    int *elelist = in->refine_elem_list;
    int elem;

    for (elem = 0; elem < in->numberofrefineelems; elem++) {
      point p1 = idx2verlist[elelist[elem*4]];
      point p2 = idx2verlist[elelist[elem*4+1]];
      point p3 = idx2verlist[elelist[elem*4+2]];
      point p4 = idx2verlist[elelist[elem*4+3]];
      
      if (!get_tet(p1, p2, p3, p4, &checktet)) {
        continue;
      }

      REAL volume_limit;
      if (in->refine_elem_vol_list != NULL) {
        volume_limit = in->refine_elem_vol_list[i];
      } else {
        point *ppt = (point *) &(checktet.tet[4]);
        REAL volume = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]) / 6.;
        volume_limit = volume / 3.;
      }
      setvolumebound(checktet.tet, volume_limit);

      //assert(check_tets_list->objects == 0l);
      triface *quetet;
      marktest2(checktet);
      check_tets_list->newindex((void **) &quetet);
      *quetet = checktet;
      
      int maxiter = 2, iter;

      for (iter = 0; iter < maxiter; iter++) {
        repairbadtets(queratio, chkencflag);

        if (later_unflip_queue->objects > 0l) {
          recoverdelaunay();
        }

        // Split unsplit tetrahedra
        long badtetcount = 0, splitcount = 0;
        int j;

        for (i = 0; i < unsplit_badtets->objects; i++) {
          badface *bt = (badface *) fastlookup(unsplit_badtets, i);
          if ((bt->tt.tet != NULL) &&
              ( org(bt->tt) == bt->forg ) &&
              (dest(bt->tt) == bt->fdest) &&
              (apex(bt->tt) == bt->fapex) &&
              (oppo(bt->tt) == bt->foppo)) {

            if (steinerleft == 0) break;
            if (elem_limit > 0) {
              if ((tetrahedrons->items - hullsize) > elem_limit) {
                break;
              }
            }

            // Count a live tet.
            badtetcount++;
            insertvertexflags ivf;
            qflag = (int) bt->key;
            point *ppt = (point *) &(bt->tt.tet[4]);
            for (j = 0; j < 3; j++) {
              param[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0;
            }
            for (; j < 6; j++) {
              param[j] = bt->cent[j];
            }
            if (split_tetrahedron(&bt->tt, param, qflag, chkencflag, ivf)) {
              splitcount++;
            }

            if (badtetrahedrons->items > 0) {
              // Push new bad quality tetrahedron into queue.
              badtetrahedrons->traversalinit();
              triface *bface = (triface *) badtetrahedrons->traverse();
              while (bface != NULL) {
                check_tets_list->newindex((void **) &quetet);
                *quetet = *bface;
                bface = (triface *) badtetrahedrons->traverse();
              }
              badtetrahedrons->restart();
            }
          }
        } // i

        unsplit_badtets->restart();

        if (splitcount == 0) break;
      } // iter

      if (check_tets_list->objects > 0) {
        // Clean the list.
        for (i = 0; i < check_tets_list->objects; i++) {
          quetet = (triface *) fastlookup(check_tets_list, i);
          if (!isdeadtet(*quetet)) {
            unmarktest2(*quetet);
          }
        }
        check_tets_list->restart();
      }

      if (steinerleft == 0) break;
      if (elem_limit > 0) {
        if ((tetrahedrons->items - hullsize) > elem_limit) {
          break;
        }
      }

    } // elem

    if (b->verbose) {
      printf("  Added %ld Steiner points.\n", points->items - steinercount);
    }
    delete [] idx2verlist;
  } // if (b->refine && (in->refine_elem_list != NULL))
  // with -r and an .elem file ================================================

  bool force_quit_refinement = false;

  if (steinerleft == 0) {
    force_quit_refinement = true;
  } else if (elem_limit > 0) {
    if ((tetrahedrons->items - hullsize) > elem_limit) {
      force_quit_refinement = true;
    }
  }

  if (!b->nobisect) { // no -Y
    bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, or -D7

    if (ref_segment && !force_quit_refinement) {
      if (b->verbose) {
        printf("  Splitting encroached subsegments.\n");
      }

      chkencflag = 1; // Only check encroaching subsegments.
      steinercount = points->items;
    
      // Add all segments into the pool.
      subsegs->traversalinit();
      checkseg.sh = shellfacetraverse(subsegs);
      while (checkseg.sh != (shellface *) NULL) {
        //enqueuesubface(badsubsegs, &checkseg);
        point encpt = NULL;
        if (check_enc_segment(&checkseg, &encpt)) {
          badface *bf = (badface *) split_segments_pool->alloc();
          bf->init();
          bf->ss = checkseg;
          bf->forg = sorg(checkseg);
          bf->fdest = sdest(checkseg);
          bf->noppo = encpt;
          // Push it into stack.
          bf->nextitem = stack_enc_segments;
          stack_enc_segments = bf;
        }
        checkseg.sh = shellfacetraverse(subsegs);
      }

      // Split all encroached segments.
      for (i = 0; i < 6; i++) param[i] = 0.0;
      qflag = 0;

      repairencsegs(param, qflag, chkencflag);

      if (b->verbose) {
        printf("  Added %ld Steiner points.\n", points->items - steinercount);
      }

      if (later_unflip_queue->objects > 0l) {
        recoverdelaunay();
      }
    } // if (ref_segment)

    bool ref_surface = ((b->cdtrefine & 2) > 0); // -D2, -D3, or -D7

    if (ref_surface && !force_quit_refinement) {
      if (b->verbose) {
        printf("  Splitting encroached and bad quality subfaces.\n");
      }

      chkencflag = 2; // only check encroaching subfaces.
      steinercount = points->items;
      bak_segref_count = st_segref_count;
      bak_facref_count = st_facref_count;

      // Add all subfaces into the pool.
      REAL ccent[3], radius;
      point encpt = NULL;

      subfaces->traversalinit();
      checksh.sh = shellfacetraverse(subfaces);
      while (checksh.sh != (shellface *) NULL) {
        //enqueuesubface(badsubfacs, &checksh);
        if (get_subface_ccent(&checksh, ccent)) {
          encpt = NULL;
          for (i = 3; i < 6; i++) param[i] = 0.0;
          if (check_enc_subface(&checksh, &encpt, ccent, &radius)) {
            enqueue_subface(&checksh, encpt, ccent, param);
          } else {
            if (check_subface(&checksh, ccent, radius, param)) {
              enqueue_subface(&checksh, NULL, ccent, param);
            }
          }
        } else {
          terminatetetgen(this, 2); // report a bug.
        }
        checksh.sh = shellfacetraverse(subfaces);
      }

      // check_enc_subface() may find some non-Delaunay faces.
      if (flippool->items > 0) {
        flipconstraints fc;
        fc.chkencflag = chkencflag;
        fc.enqflag = 2;
        lawsonflip3d(&fc);
      }

      // Split all encroached subfaces.
      for (i = 0; i < 6; i++) param[i] = 0.0;
      qflag = 0;
    
      int maxiter = 3, iter;
    
      for (iter = 0; iter < maxiter; iter++) {

        if (b->verbose > 1) {
          printf("  iter = %d\n", iter+1);
        }
        long iter_steinercount = points->items;
        long iter_segref_count = st_segref_count;
        long iter_facref_count = st_facref_count;

        repairencfacs(param, qflag, chkencflag);

        if (b->verbose > 1) {
          printf("  Added %ld (%ld,%ld) Steiner points.\n",
                 points->items-iter_steinercount,
                 st_segref_count-iter_segref_count,
                 st_facref_count-iter_facref_count);
        }

        if (unsplit_subfaces->objects > 0) {
          if (b->verbose > 1) {
            printf("  splitting %ld unsplit subfaces\n", unsplit_subfaces->objects);
          }
          int scount = 0; // Count the split subfaces.
          
          for (i = 0; i < unsplit_subfaces->objects; i++) {
            badface *bf = (badface *) fastlookup(unsplit_subfaces, i);
            if ((bf->ss.sh != NULL) &&
                ( sorg(bf->ss) == bf->forg) &&
                (sdest(bf->ss) == bf->fdest) &&
                (sapex(bf->ss) == bf->fapex)) {
              // Try to split it in its barycenter.
              int iloc, j;
              for (j = 0; j < 3; j++) {
                ccent[j] = (bf->forg[j] + bf->fdest[j] + bf->fapex[j]) / 3.;
              }
              for (j = 3; j < 6; j++) param[j] = bf->cent[j];
              encpt = bf->noppo; // The encroaching vertex.
              if (split_subface(&bf->ss, encpt, ccent, param, qflag, chkencflag, &iloc)) {
                scount++;
              }
            }
          } // i

          unsplit_subfaces->restart();

          if (b->verbose > 1) {
            printf("  Split %d subfaces.\n", scount);
          }
        } else {
          break; // no unsplit subfaces.
        } // if (unsplit_subfaces->objects > 0)
      } // iter

      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 (badsubfacs->items > 0) {
        // Clean this pool.
        badsubfacs->traversalinit();
        face *bface = (face *) badsubfacs->traverse();
        while (bface != NULL) {
          if ((bface->sh != NULL) && (bface->sh[3] != NULL)) {
            if (smarktest2ed(*bface)) {
              sunmarktest2(*bface);
            }
          }
          bface = (face *) badsubfacs->traverse();
        }
        badsubfacs->restart();
      }

      if (later_unflip_queue->objects > 0l) {
        recoverdelaunay();
      }
    } // if (ref_subface)
  } // if (!b->nobisect)

  if (((b->cdtrefine & 4) > 0) && !force_quit_refinement) { // -D4, -D5, or -D7
    // Begin of adding Steiner points in volume ===============================

    // A Steiner point can be added only if it does not encroach upon any
    //   boundary segment or subface.
    if (b->verbose) {
      printf("  Splitting bad quality tets.\n");
    }

    for (i = 0; i < 6; i++) param[i] = 0.0;
    qflag = 0;

    // Add all tetrahedra (no hull tets) into the pool.
    triface *quetet;
    tetrahedrons->traversalinit();
    checktet.tet = tetrahedrontraverse();
    while (checktet.tet != NULL) {
      marktest2(checktet);
      check_tets_list->newindex((void **) &quetet);
      *quetet = checktet;
      checktet.tet = tetrahedrontraverse();
    }


    chkencflag = 4; // Check bad tetrahedra.

    REAL queratio = b->minratio > 2. ? b->minratio : 2.0;
    queratio *= 2.0; // queratio; // increase this value.

    int maxiter = 3, iter;

    for (iter = 0; iter < maxiter; iter++) {
      steinercount = points->items;
      bak_segref_count = st_segref_count;
      bak_facref_count = st_facref_count;
      bak_volref_count = st_volref_count;

      if (b->verbose > 1) {
        printf("    iter = %d: queratio = %g\n", iter + 1, queratio);
      }
    
      // Split all bad quality tetrahedra.
      repairbadtets(queratio, 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 (later_unflip_queue->objects > 0l) {
        recoverdelaunay();
      }

      if (unsplit_badtets->objects == 0) break;

      //queratio *= 2.0; // queratio; // increase this value.

      // Split unsplit tetrahedra
      long badtetcount = 0, splitcount = 0;
      int j;

      for (i = 0; i < unsplit_badtets->objects; i++) {
        badface *bt = (badface *) fastlookup(unsplit_badtets, i);
        if ((bt->tt.tet != NULL) &&
            ( org(bt->tt) == bt->forg ) &&
            (dest(bt->tt) == bt->fdest) &&
            (apex(bt->tt) == bt->fapex) &&
            (oppo(bt->tt) == bt->foppo)) {
          if (steinerleft == 0) break;
          if (elem_limit > 0) {
            if ((tetrahedrons->items - hullsize) > elem_limit) {
              break;
            }
          }

          // Count a live tet.
          badtetcount++;
          insertvertexflags ivf;
          qflag = (int) bt->key;
          point *ppt = (point *) &(bt->tt.tet[4]);
          for (j = 0; j < 3; j++) {
            param[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0;
          }
          for (; j < 6; j++) {
            param[j] = bt->cent[j];
          }
          if (split_tetrahedron(&bt->tt, param, qflag, chkencflag, ivf)) {
            splitcount++;
          }
          if (badtetrahedrons->items > 0) {
            // Push new bad quality tetrahedron into queue.
            badtetrahedrons->traversalinit();
            triface *bface = (triface *) badtetrahedrons->traverse();
            while (bface != NULL) {
              check_tets_list->newindex((void **) &quetet);
              *quetet = *bface;
              bface = (triface *) badtetrahedrons->traverse();
            }
            badtetrahedrons->restart();
          }
        }
      } // i
      unsplit_badtets->restart();


      if (splitcount == 0) break;
    } // iter

    if (check_tets_list->objects > 0) {
      // Unmark all unchecked tetrahedra.
      for (i = 0; i < check_tets_list->objects; i++) {
        quetet = (triface *) fastlookup(check_tets_list, i);
        if (!isdeadtet(*quetet)) {
          unmarktest2(*quetet);
        }
      }
      check_tets_list->restart();
    }

    // End of adding Steiner points in volume =================================
  } // if ((b->cdtrefine & 4) > 0) {


  delete encseglist;
  delete encshlist;
  encseglist = NULL;
  encshlist = NULL;

  totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes);
  delete badsubsegs;
  badsubsegs = NULL;
  totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes);
  delete badsubfacs;
  badsubfacs = NULL;
  totalworkmemory += (split_subfaces_pool->maxitems * split_subfaces_pool->itembytes);
  delete split_subfaces_pool;
  split_subfaces_pool = NULL;
  delete split_segments_pool;
  split_segments_pool = NULL;
  delete unsplit_segments;
  delete unsplit_subfaces;
  unsplit_segments = unsplit_subfaces = NULL;

  totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes);
  delete badtetrahedrons;
  badtetrahedrons = NULL;
  totalworkmemory += (check_tets_list->totalmemory);
  delete check_tets_list;
  check_tets_list = NULL;
  delete unsplit_badtets;
  unsplit_badtets = NULL;
}

//                                                                            //
//                                                                            //
//== 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;
  REAL vol, len3;
  long flipcount, totalcount = 0l;
  long sliver_peels = 0l;
  int t1ver;
  int i;


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

    while (flipstack != (badface *) NULL) {
      // 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 != NULL);
              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.
                    // To avoid creating an "inverted" subface in the surface
                    //   Check the normals of the two new subfaces, they must
                    //   not be opposite.
                    point chk_pe =  org(neightet);
                    point chk_pd = dest(neightet);
                    point chk_pa = apex(neightet);
                    point chk_pb = oppo(neightet);
                    REAL n1[3], n2[3];
                    facenormal(chk_pa, chk_pb, chk_pe, n1, 1, NULL);
                    facenormal(chk_pb, chk_pa, chk_pd, n2, 1, NULL);
                    double dot = n1[0]*n2[0]+n1[1]*n2[1]+n1[2]*n2[2];
                    if (dot > 0.) {
                      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.
                      }
                    } // if (dot. > 0)
                  }
                }
                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]);

        // Use the length of the edge [d,e] as a reference to determine
        //   a nearly degenerated new tet.
        len3 = distance(pd, pe);
        len3 = (len3 * len3 * len3);
		int round_flag = 0; // [2017-10-20]
        // 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) {
            // Avoid creating a nearly degenerated new tet at boundary.
            //   Re-use fliptets[2], fliptets[3];
            esym(fliptets[0], fliptets[2]);
            esym(fliptets[1], fliptets[3]);
            if (issubface(fliptets[2]) || issubface(fliptets[3])) {
              vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe);
              if ((fabs(vol) / len3) < b->epsilon) {
                ori = 0.0; // Do rounding.
				round_flag = 1; // [2017-10-20]
              }
            }
          } // Rounding check
          if (ori <= 0) break;
          enextself(fliptets[0]);
          eprevself(fliptets[1]);
        }

        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;
          }
          // Count the number of interior subfaces for a valid 2-2 flip.
          int scount = 0;
          // 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++) {
            if (issubface(fliptets[i])) scount++;
            fnext(fliptets[i], fliptets[i+1]);
          }
          if (fliptets[3].tet == fliptets[0].tet) {
            // A 3-2 flip is found. "scount" must be either 0 or 2.
            if (scount == 1) {
              // This can happen during the boundary recovery. The adjacent
              //   subface is either missing or not recovered yet.
              continue;
            } else if (scount == 2) {
              // Valid if a 2-2 flip is possible.
              for (i = 0; i < 3; i++) {
                if (!issubface(fliptets[i])) break;
              }
              // Assume fliptets[i] is the tet (b,a,c,e). The two subfaces are
              //  fliptets[(i+1)%3] (b,a,e,d) and fliptets[(i+2)%3] (b,a,d,c).
              //  A 2-2 flip is possible if the two faces (d,e,a) and (e,d,b)
              //  are not subfaces.
              triface face1, face2;
              neightet = fliptets[(i+1)%3]; // (b,a,e,d)
              enext(neightet, face1);
              esymself(face1); // (e,a,d)
              eprev(neightet, face2);
              esymself(face2); // (b,e,d)
              if (issubface(face1) || issubface(face2)) {
                continue;
              }
            }
            // 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) {
              if (ori != 0.) {
                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;
                    round_flag = 1;
                  }
                } // 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]
                // There are exactly 4 tets at this edge.
                // Moreover, a,b,e,d are coplanar. This 4-4 flip will replace
                //   edge (a,b) to edge (d,e).

                // A valid 2-2 flip is when both faces (a,b,d) and (a,b,e) are
                //   subfaces, and (a,b,c) and (a,b,f) are not subfaces.
                if (issubface(fliptets[0])) { // (a,b,d)
                  if (!issubface(fliptets[2])) { // (a,b,e)
                    continue; // not valid 2-2 flip.
                  }
                  if (issubface(fliptets[1]) ||
                      issubface(fliptets[3])) {
                    continue; // The surface mesh is degnerated.
                  }
                } else {
                  if (issubface(fliptets[1]) ||
                      issubface(fliptets[2]) ||
                      issubface(fliptets[3])) {
                    continue; // not valid 2-2 flip.
                  }
                }
                
                if (round_flag == 1) {
                  //continue; // [2017-10-20]
                  // We want to flip (nearly coplanar) edges [a,b] to [d,e].
                  // Only allow this flip if all new faces are locally Delaunay.
                  // Otherwise, this routine may not terminate.
                  point pb =  org(fliptets[0]);
                  point pa = dest(fliptets[0]);
                  point pc = apex(fliptets[1]);
                  point pf = apex(fliptets[3]); // pf may be dummypoint
                  
                  if (is_collinear_at(pa, pd, pe) ||
                      is_collinear_at(pb, pd, pe)) {
                    continue; // avoid creating a degenerated (sub)face.
                  }

                  // Validate the four new tets (not inverted)
                  REAL o1, o2;
                  o1 = orient3d(pe, pd, pc, pa);
                  o2 = orient3d(pe, pd, pb, pc);
                  if ((o1 >= 0.) || (o2 >= 0.)) {
                    //assert(0); // to debug...
                    continue; // inverted new tets
                  }
                  if (pf != dummypoint) {
                    REAL o3, o4;
                    o3 = orient3d(pe, pd, pa, pf);
                    o4 = orient3d(pe, pd, pf, pb);
                    if ((o3 >= 0.) || (o4 >= 0.)) {
                      continue; // inverted new tets
                    }
                  }
                  // Validate locally Delaunay properties of new faces.
                  REAL test_sign = insphere_s(pe, pd, pc, pa, pb);
                  if (test_sign < 0) {
                    // Locally non-Delaunay. Do not perform the 4-4 flip.
                    continue;
                  }
                  if (pf != dummypoint) {
                    test_sign = insphere_s(pe, pd, pf, pb, pa);
                    if (test_sign < 0) {
                      // Locally non-Delaunay. Do not perform the 4-4 flip.
                      continue;
                    }
                  }
                } // if (round_flag == 1)
                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)
            }
          }
          // This non-Delaunay face is unflippable. Save it.
          // unflipqueue->newindex((void **) &bface);
          bface = (badface *) flippool->alloc();
          bface->init();
          esymself(fliptets[0]); // *** The original non-Delaunay face ****
          bface->tt = fliptets[0];
          bface->forg  = org(fliptets[0]);
          bface->fdest = dest(fliptets[0]);
          bface->fapex = apex(fliptets[0]);
          // Add it into the unflip queue.
          if (unflip_queue_front == NULL) {
            unflip_queue_front = bface;
          } else {
            unflip_queue_tail->nextitem = bface;
          }
          unflip_queue_tail = bface;
        } // if (ori <= 0)
      } // if (sign < 0)
    } // while (flipstack)

    if (b->verbose > 2) {
      if (flipcount > 0) {
        printf("      Performed %ld flips.\n", flipcount);
      }
      if (flippool->items > 0) {
        printf("      Saved %ld unflippbale faces.\n", flippool->items);
      }
    }
    // Accumulate the counter of flips.
    totalcount += flipcount;

    // Return if no unflippable faces left.
    //if (unflipqueue->objects == 0l) break;
    if (flippool->items == 0l) break;
    // Return if no flip has been performed.
    if (flipcount == 0l) break;

    // Try to flip the unflippable faces.
    while (unflip_queue_front != NULL) {
      bface = unflip_queue_front;
      if (!isdeadtet(bface->tt) &&
        (org(bface->tt) == bface->forg) &&
        (dest(bface->tt) == bface->fdest) &&
        (apex(bface->tt) == bface->fapex)) {
        flippush(flipstack, &(bface->tt));
      }
      unflip_queue_front = bface->nextitem;
      flippool->dealloc((void *) bface);
    }
    unflip_queue_tail = NULL;

  } // while (flippool->items != 0l)

  if (flippool->items > 0l) {
    // Save the unflippable faces to flip them later.
    badface *bf;
    while (unflip_queue_front != NULL) {
      bface = unflip_queue_front;
      if (!isdeadtet(bface->tt) &&
        (org(bface->tt) == bface->forg) &&
        (dest(bface->tt) == bface->fdest) &&
        (apex(bface->tt) == bface->fapex)) {
        //flippush(flipstack, &(bface->tt));
        later_unflip_queue->newindex((void **) &bf);
        *bf = *bface;
      }
      unflip_queue_front = bface->nextitem;
      //flippool->dealloc((void *) bface);
    }
    //unflip_queue_tail = NULL;
    flippool->restart(); // Clear the pool.
  }

  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()
{
  badface *bface, *parybface;
  flipconstraints fc;
  int i, j;

  if (b->verbose > 2) {
    printf("    Recovering Delaunayness...\n");
  }
  tetprism_vol_sum = 0.0; // Initialize it.

  if (later_unflip_queue->objects > 0) {
    // Flip the saved unflippable faces.
    for (i = 0; i < later_unflip_queue->objects; i++) {
      bface = (badface *) fastlookup(later_unflip_queue, i);
      if (!isdeadtet(bface->tt) &&
          (org(bface->tt) == bface->forg) &&
          (dest(bface->tt) == bface->fdest) &&
          (apex(bface->tt) == bface->fapex)) {
        flippush(flipstack, &(bface->tt));
      }
    }
    later_unflip_queue->restart(); // clean it.
    if (flippool->items == 0l) {
      return;
    }
  } else {
    if (flippool->items == 0l) {
      // Flip all locally non-Delaunay faces of the tetrahedralisation.
      triface tetloop, neightet; //, *parytet;
      tetrahedrons->traversalinit();
      tetloop.tet = tetrahedrontraverse();
      while (tetloop.tet != NULL) {
        for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
          decode(tetloop.tet[tetloop.ver], neightet);
          if (!facemarked(neightet)) {
            flippush(flipstack, &tetloop);
          }
        }
        point *ppt = (point *) &(tetloop.tet[4]);
        tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]);
        tetloop.tet = tetrahedrontraverse();
      }
    }
  }
  
  recover_delaunay_count++;

  // 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 > 2) {
    printf("    Initial obj = %.17g\n", tetprism_vol_sum);
  }

  if (b->verbose > 2) {
    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 > 2) {
    printf("    obj (after Lawson) = %.17g\n", tetprism_vol_sum);
  }

  if (later_unflip_queue->objects == 0l) {
    return;
  }
  
  fc.unflip = 0; // fc.unflip = 1; // Unflip if the edge is not flipped.
  fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'.
  fc.enqflag = 0;

  int bak_autofliplinklevel = autofliplinklevel; 
  int bak_fliplinklevel = b->fliplinklevel;
  autofliplinklevel = 1; // Init level.
  b->fliplinklevel = -1; // No fixed level.

  badface *bfarray = new badface[later_unflip_queue->objects];

  while ((later_unflip_queue->objects > 0) &&
         (autofliplinklevel < 4)) { // level = 1,2,3 //< 10

    int nbf = later_unflip_queue->objects;
    for (i = 0; i < nbf; i++) {
      bfarray[i] = * (badface *) fastlookup(later_unflip_queue, i);
    }
    later_unflip_queue->restart(); // clean it.

    if (b->verbose > 2) {
      printf("    Recover Delaunay [level = %2d] #:  %d.\n",
             autofliplinklevel, nbf);
    }

    for (i = 0; i < nbf; i++) {
      bface = &(bfarray[i]);
      if (getedge(bface->forg, bface->fdest, &bface->tt)) {
        if (removeedgebyflips(&(bface->tt), &fc) == 2) {
          tetprism_vol_sum += fc.tetprism_vol_sum;
        } else {
          // This edge is not removed. Save it in later_flip_queue.
          later_unflip_queue->newindex((void **) &parybface);
          *parybface = bfarray[i]; // *bface;
        }
        fc.tetprism_vol_sum = 0.0; // Clear it.
        if (cavetetlist->objects > 0) {
          // Queue new faces for flips.
          triface neightet, *parytet;
          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();
        } // if (cavetetlist->objects > 0)
      }
    } // i

    autofliplinklevel++; // =b->fliplinklevelinc;
  } // while (later_unflip_queue->objects > 0)

  delete [] bfarray;

  if (b->verbose > 2) {
    if (later_unflip_queue->objects > 0l) {
      printf("    %ld non-Delaunay edges remained.\n", later_unflip_queue->objects);
    }
  }
  
  if (flippool->items > 0l) {
    // Flip locally non-Delaunay faces. Unflippable faces are queued
    //   in later_flip_queue.
    fc.remove_ndelaunay_edge = 1;
    fc.enqflag = 2; // queue exteior faces of a flip.
    lawsonflip3d(&fc);
    //fc.enqflag = 0; // for removedgebyflips().
  }

  if (b->verbose > 2) {
    printf("  Final obj  = %.17g\n", tetprism_vol_sum);
  }

  if (later_unflip_queue->objects > 0l) {
    if (b->verbose > 2) {
      printf("    %ld non-Delaunay edges remained.\n", later_unflip_queue->objects);
    }
    later_unflip_queue->restart();
  }

  autofliplinklevel = bak_autofliplinklevel; // Restore this value.
  b->fliplinklevel = bak_fliplinklevel;
}

//============================================================================//
//                                                                            //
// get_seg_laplacian_center()    Get the Laplcian center of a mesh vertex.    //
//                                                                            //
// "mesh_vert" must be a Steiner vertex (FREESEGVERTEX) in a segment.         //
//                                                                            //
//============================================================================//

int tetgenmesh::get_seg_laplacian_center(point mesh_vert, REAL target[3])
{
  if (pointtype(mesh_vert) == UNUSEDVERTEX) {
    return 0;
  }

  face leftseg, rightseg;

  sdecode(point2sh(mesh_vert), leftseg);
  leftseg.shver = 0;
  if (sdest(leftseg) == mesh_vert) {
    senext(leftseg, rightseg);
    spivotself(rightseg);
    rightseg.shver = 0;
    if (sorg(rightseg) != mesh_vert) {
      sesymself(rightseg);
    }
    if (sorg(rightseg) != mesh_vert) {
      terminatetetgen(this, 2);
    }
  } else {
    rightseg = leftseg;
    senext2(rightseg, leftseg);
    spivotself(leftseg);
    leftseg.shver = 0;
    if (sdest(leftseg) != mesh_vert) {
      sesymself(leftseg);
    }
    if (sdest(leftseg) != mesh_vert) {
      terminatetetgen(this, 2);
    }
  }
  point lpt = sorg(leftseg);
  point rpt = sdest(rightseg);

  int j;
  
  for (j = 0; j < 3; j++) {
    target[j] = 0.5 * (lpt[j] + rpt[j]);
  }
  
  return 1;
}

//============================================================================//
//                                                                            //
// get_surf_laplacian_center()    Get the Laplcian center of a mesh vertex.   //
//                                                                            //
// "mesh_vert" must be a Steiner vertex (FREEFACETVERTEX) in a facet.         //
//                                                                            //
//============================================================================//

int tetgenmesh::get_surf_laplacian_center(point mesh_vert, REAL target[3])
{
  if (pointtype(mesh_vert) == UNUSEDVERTEX) {
    return 0;
  }

  getvertexstar(1, mesh_vert, caveoldtetlist, NULL, caveshlist);

  // The number of vertices is the same as the number of edges.
  int npt = (int) caveshlist->objects;
  int i, j;
  
  for (j = 0; j < 3; j++) {
    target[j] = 0.;
  }

  for (i = 0; i < npt; i++) {
    face *cavesh = (face *) fastlookup(caveshlist, i);
    point e1 =  sorg(*cavesh);
    point e2 = sdest(*cavesh);
    for (j = 0; j < 3; j++) {
      target[j] += e1[j];
    }
    for (j = 0; j < 3; j++) {
      target[j] += e2[j];
    }
  }

  // We added every link vertex twice.
  int npt2 = npt * 2;

  for (j = 0; j < 3; j++) {
    target[j] /= (double) npt2;
  }

  caveoldtetlist->restart();
  caveshlist->restart();
  return 1;
}

//============================================================================//
//                                                                            //
// get_laplacian_center()    Get the Laplcian center of a mesh vertex.        //
//                                                                            //
// "mesh_vert" must be a Steiner vertex (FREEVOLVERTEX) in volume.            //
//                                                                            //
//============================================================================//

int tetgenmesh::get_laplacian_center(point mesh_vert, REAL target[3])
{
  if (pointtype(mesh_vert) == UNUSEDVERTEX) {
    return 0;
  }
  getvertexstar(1, mesh_vert, caveoldtetlist, cavetetvertlist, NULL);

  // Calculate the laplacian center.
  int npt = (int) cavetetvertlist->objects;
  int i, j;

  for (j = 0; j < 3; j++) {
    target[j] = 0.;
  }

  for (i = 0; i < npt; i++) {
    point *pt = (point *) fastlookup(cavetetvertlist, i);
    for (j = 0; j < 3; j++) {
      target[j] += (*pt)[j];
    }
  }

  for (j = 0; j < 3; j++) {
    target[j] /= (double) npt;
  }

  cavetetvertlist->restart();
  return 1;
}

//============================================================================//
//                                                                            //
// move_vertex()    Try to move a given vertex towards the target position.   //
//                                                                            //
//============================================================================//

bool tetgenmesh::move_vertex(point mesh_vert, REAL target[3])
{
  if (pointtype(mesh_vert) == UNUSEDVERTEX) {
    if (caveoldtetlist->objects > 0l) {
      caveoldtetlist->restart();
    }
    return 0;
  }
  // Do not move if the target is already very close the vertex.
  if (distance(mesh_vert, target) < minedgelength) {
    if (caveoldtetlist->objects > 0l) {
      caveoldtetlist->restart();
    }
    return 0;
  }
  triface* cavetet;
  point pa, pb, pc;
  REAL ori;
  int i, j;

  REAL dir[3], newpos[3];
  REAL alpha = b->smooth_alpha; // 0.3;

  for (j = 0; j < 3; j++) {
    dir[j] = target[j] - mesh_vert[j];
    newpos[j] = mesh_vert[j] + alpha * dir[j];
  }

  if (caveoldtetlist->objects == 0l) {
    getvertexstar(1, mesh_vert, caveoldtetlist, NULL, NULL);
  }

  bool moveflag = true;
  int iter = 0;

  while (iter < 3) {
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      if (ishulltet(*cavetet)) continue; // Skip a hull face.

      pa =  org(*cavetet);
      pb = dest(*cavetet);
      pc = apex(*cavetet);
      ori = orient3d(pa, pb, pc, newpos);
      if (ori >= 0) {
        moveflag = false;
        break; // This tet becomes invalid.
      }
    }
    if (moveflag) {
      break;
    } else {
      alpha = (alpha / 2.);
      for (j = 0; j < 3; j++) {
        newpos[j] = mesh_vert[j] + alpha * dir[j];
      }
      iter++;
    }
  } // while (iter < 3)

  if (moveflag) {
    for (j = 0; j < 3; j++) {
      mesh_vert[j] = newpos[j];
    }

    triface checkface, neightet;
    //int j;

    // Push all faces of this vertex star and link into queue.
    for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      if (ishulltet(*cavetet)) continue; // Skip a hull face.
      flippush(flipstack, cavetet);
      for (j = 0; j < 3; j++) {
        esym(*cavetet, checkface);
        fsym(checkface, neightet);
        if (!facemarked(neightet)) {
          flippush(flipstack, &checkface);
        }
        enextself(*cavetet);
      }
    }

    if (badtetrahedrons != NULL) {
      // queue all cavity tets for quality check.
      for (i = 0; i < caveoldtetlist->objects; i++) {
        cavetet = (triface *) fastlookup(caveoldtetlist, i);
        if (ishulltet(*cavetet)) continue; // Skip a hull face.
        enqueuetetrahedron(cavetet);
      }
    }

    flipconstraints fc;
    fc.enqflag = 2; // queue all exterior faces of a flip.
    if (badtetrahedrons != NULL) {
      fc.chkencflag = 4; // queue new tets for quality check.
    }
    lawsonflip3d(&fc);


  } // if (moveflag)

  caveoldtetlist->restart();
  return moveflag;
}

//============================================================================//
//                                                                            //
// smooth_vertices()    Smooth vertices.                                      //
//                                                                            //
//============================================================================//

void tetgenmesh::smooth_vertices()
{
  if (!b->quiet) {
    printf("Smoothing vertices...\n");
  }

  if (b->verbose) {
    printf("  Smooth criterion   = %d\n", b->smooth_cirterion);
    printf("  Smooth iterations  = %d\n", b->smooth_maxiter);
    printf("  Smooth relax-alpha = %g\n", b->smooth_alpha);
  }

  point *smpt_list = NULL;
  point *surf_smpt_list = NULL;
  point *seg_smpt_list = NULL;
  int volcount = 0, faccount = 0, segcount = 0;

  // Only use it when we have Steiner points.
  if (st_segref_count > 0) {
    seg_smpt_list = new point[st_segref_count];
  }
  if (st_volref_count > 0) {
    smpt_list = new point[st_volref_count];
  }
  if (st_facref_count > 0) {
    surf_smpt_list = new point[st_facref_count];
  }

  points->traversalinit();
  point ptloop = pointtraverse();
  while (ptloop != NULL) {
    enum verttype vt = pointtype(ptloop);
    if (vt == FREEVOLVERTEX) {
      smpt_list[volcount++] = ptloop;
    } else if (vt == FREEFACETVERTEX) {
      surf_smpt_list[faccount++] = ptloop;
    } else if (vt == FREESEGVERTEX) {
      seg_smpt_list[segcount++] = ptloop;
    }
    ptloop = pointtraverse();
  }

  if ((volcount != st_volref_count) ||
      (faccount != st_facref_count) ||
      (segcount != st_segref_count)) {
    terminatetetgen(this, 2);
  }

  if (b->verbose > 1) {
    printf("  Smoothing (%ld, %ld) %ld vertices.\n",
           st_segref_count, st_facref_count, st_volref_count);
  }

  // Allocate a list of target points.
  REAL *target_list = NULL;
  REAL *surf_target_list = NULL;
  REAL *seg_target_list = NULL;

  if (st_volref_count > 0) {
    target_list = new REAL[st_volref_count * 3];
  }
  if (st_facref_count > 0) {
    surf_target_list = new REAL[st_facref_count * 3];
  }
  if (st_segref_count > 0) {
    seg_target_list = new REAL[st_segref_count * 3];
  }

  long bak_flipcount = flip23count + flip32count + flip44count;
  int  movedcount = 0, total_movecount = 0;
  int  unmovedcount = 0, total_unmovedcount = 0;
  int  iter = 0, maxiter = b->smooth_maxiter;
  int  i;

  for (iter = 0; iter < maxiter; iter++) {

    movedcount = unmovedcount = 0;

    if (((b->smooth_cirterion & 4) > 0)) { // -s4, -s5, -s6, -s7, default -s3
      //if (st_segref_count > 0) {
      for (i = 0; i < st_segref_count; i++) {
        get_seg_laplacian_center(seg_smpt_list[i], &(seg_target_list[i*3]));
      }
      for (i = 0; i < st_segref_count; i++) {
        if (move_vertex(seg_smpt_list[i], &(seg_target_list[i*3]))) {
          if (later_unflip_queue->objects > b->unflip_queue_limit) {
            recoverdelaunay();
          }
          movedcount++;
        } else {
          unmovedcount++;
        }
      }
      //} // if (st_segref_count > 0)
    }

    if (((b->smooth_cirterion & 2) > 0)) { // default -s3
      //if (st_facref_count > 0) {
      for (i = 0; i < st_facref_count; i++) {
        get_surf_laplacian_center(surf_smpt_list[i], &(surf_target_list[i*3]));
      }
      for (i = 0; i < st_facref_count; i++) {
        if (move_vertex(surf_smpt_list[i], &(surf_target_list[i*3]))) {
          if (later_unflip_queue->objects > b->unflip_queue_limit) {
            recoverdelaunay();
          }
          movedcount++;
        } else {
          unmovedcount++;
        }
      }
      //} // if (st_facref_count > 0)
    }

    if (((b->smooth_cirterion & 1) > 0)) { // default -s3
      //if (st_volref_count > 0) {
      for (i = 0; i < st_volref_count; i++) {
        get_laplacian_center(smpt_list[i], &(target_list[i*3]));
        caveoldtetlist->restart();
      }
      for (i = 0; i < st_volref_count; i++) {
        if (move_vertex(smpt_list[i], &(target_list[i*3]))) {
          if (later_unflip_queue->objects > b->unflip_queue_limit) {
            recoverdelaunay();
          }
          movedcount++;
        } else {
          unmovedcount++;
        }
      }
      //} // if (st_volref_count > 0)
    }

    if (movedcount == 0) break;

    if (b->verbose > 1) {
      printf("  iter=%d, smoothed %d vertices, %d unsmoothed\n", iter,
             movedcount, unmovedcount);
    }


    total_movecount += movedcount;
    total_unmovedcount += unmovedcount;
    
    if (later_unflip_queue->objects > 0) {
      recoverdelaunay();
    }
  } // iter

  if (b->verbose > 1) {
    printf("  Smoothed %d (%d) times, flipped %ld faces.\n",
           total_movecount, total_unmovedcount,
           flip23count + flip32count + flip44count - bak_flipcount);
  }

  if (st_segref_count > 0) {
    delete [] seg_smpt_list;
    delete [] seg_target_list;
  }
  if (st_facref_count > 0) {
    delete [] surf_target_list;
    delete [] surf_smpt_list;
  }
  if (st_volref_count > 0) {
    delete [] target_list;
    delete [] smpt_list;
  }
}

//============================================================================//
//                                                                            //
// get_tet()    Get the tetrahedron with the given vertices.                  //
//                                                                            //
//============================================================================//

bool tetgenmesh::get_tet(point pa, point pb, point pc, point pd, triface *searchtet)
{
  if (getedge(pa, pb, searchtet)) {
    int t1ver;
    triface 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 true;
      } else {
        fsymself(*searchtet);
        if (oppo(*searchtet) == pd) {
          return true;
        }
      }
    }
  }

  return false;
}

//============================================================================//
//                                                                            //
// get_tetqual()    Calculate various quality measures of a given tetrahedron.//
//                                                                            //
// Calculate the aspect ratio (Lmax / hmin), edge ratio (Lmax/Lmin), maximal  //
// and minimal dihedral angles of this tetrahedron.                           //
//                                                                            //
// These values are returned by:                                              //
//   bf->key,  aspect ratio                                                   //
//   bf->cent[0], cosine of maximal dihedral angle                            //
//   bf->cent[1], cosine of minimal dihedral angle                            //
//   bf->cent[2], edge ratio                                                  //
//   bf->cent[3], minimal edge length                                         //
//   bf->cent[4], volume (used to validate whether it is modified or not).    //
//   bf->cent[5], (no use).                                                   //
//   bf->tet, the edge with maximal dihedral angle.                           //
//   bf->ss.shver, (re-used) count the number of dihedrals > 165 degree.      //
//                                                                            //
//============================================================================//

bool tetgenmesh::get_tetqual(triface *chktet, point oppo_pt, badface *bf)
{
  if (chktet != NULL) {
    bf->init();
    if (oppo_pt == NULL) {
      point *ppt = (point *) &(chktet->tet[4]);
      bf->forg  = ppt[0]; // pa
      bf->fdest = ppt[1]; // pb
      bf->fapex = ppt[2]; // pc
      bf->foppo = ppt[3]; // pd
    } else {
      bf->forg  =  org(*chktet);
      bf->fdest = dest(*chktet);
      bf->fapex = apex(*chktet);
      bf->foppo = oppo_pt;
    }
  }

  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] =  bf->forg[i] - bf->foppo[i];  // d->a vec
  for (i = 0; i < 3; i++) A[1][i] = bf->fdest[i] - bf->foppo[i];  // d->b vec
  for (i = 0; i < 3; i++) A[2][i] = bf->fapex[i] - bf->foppo[i];  // d->c vec

  // Get the max-min edge length
  REAL L[6], Lmax, Lmin;
  REAL Vab[3], Vbc[3], Vca[3];

  for (i = 0; i < 3; i++) Vab[i] = bf->fdest[i] - bf->forg[i];   // a->b vec
  for (i = 0; i < 3; i++) Vbc[i] = bf->fapex[i] - bf->fdest[i];  // b->c vec
  for (i = 0; i < 3; i++) Vca[i] = bf->forg[i]  - bf->fapex[i];  // c->a vec

  // Use the idx2edge
  L[0] = dot(A[2], A[2]); // edge c,d
  L[1] = dot(A[0], A[0]); // edge a,d
  L[2] = dot(Vab, Vab);   // edge a,b
  L[3] = dot(Vbc, Vbc);   // edge b,c
  L[4] = dot(A[1], A[1]); // edge b,d
  L[5] = dot(Vca, Vca);   // edge a,c

  Lmax = Lmin = L[0];
  //int idx = 0;
  for (i = 1; i < 6; i++) {
    Lmax = (Lmax < L[i] ? L[i] : Lmax);
    Lmin = (Lmin > L[i] ? L[i] : Lmin);
    //if (Lmin > L[i]) {
    //  Lmin = L[i]; idx = i;
    //}
  }

  Lmax = sqrt(Lmax);
  Lmin = sqrt(Lmin);

  // Caluclate the Lmax / Lmin edge ratio (to detect very short edge).
  bf->cent[2] = Lmax / Lmin;
  bf->cent[3] = Lmin;

  // Calculate the normals and heights.
  REAL N[4][3]; // The normals of the four faces.
  REAL H[4]; // H[i] is the inverse of the height of its corresponding face.
  bool flat_flag = false;

  if (lu_decmp(A, 3, indx, &D, 0)) {
    // Get the volume of this tet.
    bf->cent[4] = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]));
    if (bf->cent[4] > 0.0) {
      // Compute the inverse of matrix A, to get 3 normals of the 4 faces.
      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 {
      // This is a very flat tet.
      flat_flag = true;
    }
  } else {
    flat_flag = true;
  }
  
  if (flat_flag) {
    // This tet is nearly degenerate.
    bf->cent[4] = orient3d(bf->fdest, bf->forg, bf->fapex, bf->foppo);
    if (bf->cent[4] <= 0.0) {
      return false; // degenerated or inverted.
    }
    // Calculate the normals of the four faces.
    facenormal(bf->fapex, bf->fdest, bf->foppo, N[0], 1, NULL); // face [c,b,d]
    facenormal(bf->forg,  bf->fapex, bf->foppo, N[1], 1, NULL); // face [a,c,d]
    facenormal(bf->fdest, bf->forg,  bf->foppo, N[2], 1, NULL); // face [b,a,d]
    facenormal(bf->forg,  bf->fdest, bf->fapex, N[3], 1, NULL); // face [a,b,c]
  } // if (!success)

  // Normalized the normals.
  for (i = 0; i < 4; i++) {
    H[i] = sqrt(dot(N[i], N[i]));
    if (H[i] > 0.0) {
      for (j = 0; j < 3; j++) N[i][j] /= H[i];
    } else {
      return false; // H[i] == 0.0;
    }
  }

  if (!flat_flag) {
    // Get the biggest H[i] (corresponding to the smallest height).
    REAL minheightinv = H[0];
    for (i = 1; i < 4; i++) {
      if (H[i] > minheightinv) minheightinv = H[i];
    }
    // Calulcate the aspect ratio = L_max / h_min.
    bf->key = Lmax * minheightinv;
  } else {
    // A very flat tet.
    //if (bf->key <= 0.0) {
      bf->key = 1.e+30; // infinity.
    //}
  }

  // Calculate the cosine of the dihedral angles of the edges.
  REAL cosmaxd = 1.0, cosmind = -1.0, cosd;
  int f1, f2, idx = 0;
  bf->ss.shver = 0; // // Count the number of large dihedrals.
  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.
    // cosmaxd = cosd < cosmaxd ? cosd : cosmaxd;
    if (cosd < cosmaxd) {cosmaxd = cosd; idx = i;}
    cosmind = (cosd > cosmind ? cosd : cosmind);
    // Count the number of large dihedrals.
    if (cosd < cos_large_dihed) bf->ss.shver++;
  } // i

  bf->cent[0] = cosmaxd;
  bf->cent[1] = cosmind;

  // Remember the edge with largest dihedral angle.
  if (chktet) bf->tt.tet = chktet->tet;
  bf->tt.ver = edge2ver[idx];

  bf->cent[5] = 0.0;

  return true;
}

bool tetgenmesh::get_tetqual(point pa, point pb, point pc, point pd, badface *bf)
{
  bf->init();

  bf->forg  = pa;
  bf->fdest = pb;
  bf->fapex = pc;
  bf->foppo = pd;

  return get_tetqual(NULL, NULL, bf);
}

//============================================================================//
//                                                                            //
// enqueue_badtet()    Push a bad-quality tet into the proority queue.        //
//                                                                            //
//============================================================================//

void tetgenmesh:: enqueue_badtet(badface *bf)
{
  badface *bt = (badface *) badqual_tets_pool->alloc();

  *bt = *bf;

  // The following vertices are used by get_tet(...) to identify whether
  //   the saved tet is still alive or not.
  //bt->forg  =  org(bf->tt);
  //bt->fdest = dest(bf->tt);
  //bt->fapex = apex(bf->tt);
  //bt->foppo = oppo(bf->tt);

  bt->nextitem = NULL; // important, this pointer is used to recongise the last
                       //   item in each queue.

  // Push it into the priority queue.
  REAL qual = 1.0 / log(bf->key);

  // Determine the appropriate queue to put the bad subface into.
  int queuenumber = 0;
  if (qual < 1.0) {
    queuenumber = (int) (64.0 * (1.0 - qual));
    if (queuenumber > 63) {
      queuenumber = 63;
    }
  } else {
    // It's not a bad shape; put the subface in the lowest-priority queue.
    queuenumber = 0;
  }
  
  // Are we inserting into an empty queue?
  if (bt_queuefront[queuenumber] == (badface *) NULL) {
    // Yes, we are inserting into an empty queue.
    //   Will this become the highest-priority queue?
    if (queuenumber > bt_firstnonemptyq) {
      // Yes, this is the highest-priority queue.
      bt_nextnonemptyq[queuenumber] = bt_firstnonemptyq;
      bt_firstnonemptyq = queuenumber;
    } else {
      // No, this is not the highest-priority queue.
      //   Find the queue with next higher priority.
      int i = queuenumber + 1;
      while (bt_queuefront[i] == (badface *) NULL) {
        i++;
      }
      // Mark the newly nonempty queue as following a higher-priority queue.
      bt_nextnonemptyq[queuenumber] = bt_nextnonemptyq[i];
      bt_nextnonemptyq[i] = queuenumber;
    }
    // Put the bad subface at the beginning of the (empty) queue.
    bt_queuefront[queuenumber] = bt;
  } else {
    // Add the bad tetrahedron to the end of an already nonempty queue.
    bt_queuetail[queuenumber]->nextitem = bt;
  }
  // Maintain a pointer to the last subface of the queue.
  bt_queuetail[queuenumber] = bt;
}

//============================================================================//
//                                                                            //
// top_badtet()    Get a bad-quality tet from the priority queue.           //
//                                                                            //
//============================================================================//

tetgenmesh::badface* tetgenmesh::top_badtet()
{
  // Keep a record of which queue was accessed in case dequeuebadtetra()
  //   is called later.
  bt_recentq = bt_firstnonemptyq;
  // If no queues are nonempty, return NULL.
  if (bt_firstnonemptyq < 0) {
    return (badface *) NULL;
  } else {
    // Return the first tetrahedron of the highest-priority queue.
    return bt_queuefront[bt_firstnonemptyq];
  }
}

//============================================================================//
//                                                                            //
// dequeue_badtet()    Popup a bad-quality tet from the priority queue.       //
//                                                                            //
//============================================================================//
 
void tetgenmesh::dequeue_badtet()
{
  badface *bt;
  int i;

  // If queues were empty last time topbadtetra() was called, do nothing.
  if (bt_recentq >= 0) {
    // Find the tetrahedron last returned by topbadtetra().
    bt = bt_queuefront[bt_recentq];
    // Remove the tetrahedron from the queue.
    bt_queuefront[bt_recentq] = bt->nextitem;
    // If this queue is now empty, update the list of nonempty queues.
    if (bt == bt_queuetail[bt_recentq]) {
      // Was this the highest-priority queue?
      if (bt_firstnonemptyq == bt_recentq) {
        // Yes; find the queue with next lower priority.
        bt_firstnonemptyq = bt_nextnonemptyq[bt_firstnonemptyq];
      } else {
        // No; find the queue with next higher priority.
        i = bt_recentq + 1;
        while (bt_queuefront[i] == (badface *) NULL) {
          i++;
        }
        bt_nextnonemptyq[i] = bt_nextnonemptyq[bt_recentq];
      }
    }
    // Return the badface to the pool.
    badqual_tets_pool->dealloc((void *) bt);
  }
}


//============================================================================//
//                                                                            //
// add_steinerpt_to_repair()    Add Steiner to repair a bad-qaulity tet.      //
//                                                                            //
//============================================================================//

bool tetgenmesh::add_steinerpt_to_repair(badface *bf, bool bSmooth)
{
  REAL cosmaxd = bf->cent[0];
  REAL eta = bf->cent[2];
  int lcount = bf->ss.shver; // the number of large dihedrals.

  triface splittet;
  splittet.tet = NULL;

  if (cosmaxd < cosslidihed) { // cossmtdihed
    // It is a sliver (flat) (might contain a short edge -- skinny).
    triface sliver_edge;
    char shape = 0;

    // Determine the outer shape of this sliver, i.e., a square of a triangle?
    if (lcount == 2) {
      // It is a square. Try to remove the edge [a,b]
      shape = 'S';
      sliver_edge = bf->tt;
    } else if (lcount == 3) {
      // It is a triangle. Try to remove the edge [c,d]
      shape = 'T';
      edestoppo(bf->tt, sliver_edge); // face [c,d,a]
    }

    // Determine a Steiner point according to the shape of this sliver.
    if (shape == 'S') {
      REAL vol, max_vol = 0.0;

      triface check_sliver = sliver_edge;
      for (int i = 0; i < 2; i++) {
        bool is_bdry = false;
        if (issubseg(check_sliver)) {
          is_bdry = true;
        } else {
          triface spintet = check_sliver;
          int t1ver;
          do {
            if (issubface(spintet)) {
              is_bdry = true; break;
            }
            fnextself(spintet);
          } while (spintet.tet != check_sliver.tet);
        }
        
        if (!is_bdry) {
          triface spintet = check_sliver;
          int t1ver;
          do {
            point *ppt = (point *) &(spintet.tet[4]);
            vol = orient3d(ppt[1], ppt[0], ppt[2], ppt[3]);
            if (vol > max_vol) {
              max_vol = vol;
              splittet = spintet;
            }
            fnextself(spintet);
          } while (spintet.tet != check_sliver.tet);
        }
          
        // Check the opposite edge.
        edestoppoself(check_sliver);
      } // i
    } else if (shape == 'T') {
    }
  } else if (eta > b->opt_max_edge_ratio) {
    // It is a skinny tet.
    // This tet contains a relatively short edge. Check if it can be collapsed.
    REAL Lmin = bf->cent[3];

    // Get the shortest edge of this tet.
    triface short_edge = bf->tt;
    int i;
    for (i = 0; i < 6; i++) {
      short_edge.ver = edge2ver[i];
      REAL dd = distance(org(short_edge), dest(short_edge));
      if ((fabs(Lmin - dd) / Lmin) < 1e-4) break;
    }
    if (i == 6) {
      terminatetetgen(this, 2);
    }

    if (Lmin <= minedgelength) {
      // A very short edge. Check if it was correctly created.
      point e1 = org(short_edge);
      point e2 = dest(short_edge);
      if (issteinerpoint(e1)) {
        if (!create_a_shorter_edge(e1, e2)) {
          terminatetetgen(this, 2);
        }
      } else if (issteinerpoint(e2)) {
        if (!create_a_shorter_edge(e2, e1)) {
          terminatetetgen(this, 2);
        }
      }
    }
  }

  if (splittet.tet == NULL) {
    return false; // not added.
  }

  // Do not add if the splittet is also a bad qual tet.
  badface tmpbf;
  if (get_tetqual(&splittet, NULL, &tmpbf)) {
    if (tmpbf.cent[0] < cosslidihed) {
      return false;
    }
  } else {
    return false;
  }

  point steinerpt;
  makepoint(&steinerpt, FREEVOLVERTEX);
  point *ppt = (point *) &(splittet.tet[4]);
  for (int j = 0; j < 3; j++) {
    steinerpt[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0;
  }

  insertvertexflags ivf;

  //triface searchtet = splittet;
  ivf.iloc = (int) OUTSIDE;
  ivf.bowywat = 3;
  ivf.lawson = 2;
  ivf.rejflag = 0;
  if (badtetrahedrons != NULL) {
    ivf.chkencflag = 4; // queue new tets.
  }
  ivf.sloc = ivf.sbowywat = 0; // No use.
  ivf.splitbdflag = 0; // No use (its an interior vertex).
  ivf.validflag = 1;
  ivf.respectbdflag = 1;

  ivf.smlenflag = 1; // avoid creating very short edges
  ivf.parentpt = NULL; // init.

  if (insertpoint(steinerpt, &splittet, NULL, NULL, &ivf)) {
    st_volref_count++;
    //if (steinerleft > 0) steinerleft--;

    if (flipstack != NULL) {
      flipconstraints fc;
      fc.enqflag = 2;
      if (badtetrahedrons != NULL) {
        fc.chkencflag = 4;
      }
      lawsonflip3d(&fc);
    }

    if (later_unflip_queue->objects > b->unflip_queue_limit) {
      //recoverdelaunay();
      later_unflip_queue->restart(); // clean it.
    }
  } else {
    // Point is not inserted.
    pointdealloc(steinerpt);
    return false;
  }

  if (bSmooth) {
    REAL ccent[3];
    get_laplacian_center(steinerpt, ccent);
    if (move_vertex(steinerpt, ccent)) {
      opt_smooth_count++;
    }
  } // if (bSmooth)

  if (badtetrahedrons->items > 0) {
    // Push new bad quality tetrahedron into queue.
    badface bf;
    REAL max_asp = 0., cosmaxd = 1.;
    badtetrahedrons->traversalinit();
    triface *bface = (triface *) badtetrahedrons->traverse();
    while (bface != NULL) {
      if (!isdeadtet(*bface)) {
        // A queued tet may have been processed.
        if (marktest2ed(*bface)) {
          unmarktest2(*bface);
          if (!ishulltet(*bface)) {
            get_tetqual(bface, NULL, &bf);
            // Save the worst quality.
            max_asp = (max_asp > bf.key ? max_asp : bf.key);
            cosmaxd = (cosmaxd < bf.cent[0] ? cosmaxd : bf.cent[0]);
            if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) {
              bf.forg  =  org(bf.tt);
              bf.fdest = dest(bf.tt);
              bf.fapex = apex(bf.tt);
              bf.foppo = oppo(bf.tt);
              enqueue_badtet(&bf);
            }
          } // if (!ishulltet(*bface))
        }
      }
      bface = (triface *) badtetrahedrons->traverse();
    }
    badtetrahedrons->restart();
  }

  // Check if the bad quality tet is removed or not.
  if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) {
    // Try to remove it.
    if (repair_tet(bf, true, false, false)) {
      return true;
    }
  } else {
    // This tet is removed.
    return true;
  }

  return false; // not added.
}






//============================================================================//
//                                                                            //
// flip_edge_to_improve()    Flip an edge of a bad-quality tet.               //
//                                                                            //
//============================================================================//

bool tetgenmesh::flip_edge_to_improve(triface *sliver_edge, REAL& improved_cosmaxd)
{
  if (issubseg(*sliver_edge)) {
    return false;
  }

  flipconstraints fc;

  //fc.noflip_in_surface = 1; // do not flip in surface.
  fc.noflip_in_surface = ((b->nobisect > 0) || ((b->cdtrefine & 2) == 0));
  fc.remove_large_angle = 1;
  fc.unflip = 1;
  fc.collectnewtets = 1;
  fc.checkflipeligibility = 1;
  fc.cosdihed_in = improved_cosmaxd; // cosmaxd;
  fc.cosdihed_out = 0.0; // 90 degree.
  fc.max_asp_out = 0.0;

  if (removeedgebyflips(sliver_edge, &fc) == 2) {
    // This sliver is removed by flips.
    if ((fc.cosdihed_out < cosmaxdihed) || (fc.max_asp_out > b->opt_max_asp_ratio)) {
      // Queue new bad tets for further improvements.
      badface bf;
      for (int j = 0; j < cavetetlist->objects; j++) {
        triface *parytet = (triface *) fastlookup(cavetetlist, j);
        if (!isdeadtet(*parytet) && !ishulltet(*parytet)) {
          if (get_tetqual(parytet, NULL, &bf)) {
            if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) {
              bf.forg  =  org(bf.tt);
              bf.fdest = dest(bf.tt);
              bf.fapex = apex(bf.tt);
              bf.foppo = oppo(bf.tt);
              enqueue_badtet(&bf);
            }
          } else {
            terminatetetgen(this, 2);
          }
        }
      } // j
    }
    cavetetlist->restart();
    return true;
  }

  return false;
}

//============================================================================//
//                                                                            //
// repair_tet()    Repair a bad-qaulity tet.                                  //
//                                                                            //
//============================================================================//

bool tetgenmesh::repair_tet(badface *bf, bool bFlips, bool bSmooth, bool bSteiners)
{
  REAL cosmaxd = bf->cent[0];
  REAL eta = bf->cent[2];
  int lcount = bf->ss.shver; // the number of large dihedrals.

  if (cosmaxd < cossmtdihed) {
    // It is a sliver (flat) (it might contain a short edge -- skinny).
    //triface sliver_edge;
    char shape = '0';

    // Determine the outer shape of this sliver, i.e., a square of a triangle?
    if (lcount == 2) {
      // It is a square. Try to remove the edge [a,b]
      shape = 'S';
    } else if (lcount == 3) {
      // It is a triangle. Try to remove the edge [c,d]
      shape = 'T';
      //edestoppo(bf->tt, sliver_edge); // face [c,d,a]
    }

    if (bFlips) {
      if (shape == 'S') {
        triface sliver_edge = bf->tt;
        if (flip_edge_to_improve(&sliver_edge, cosmaxd)) {
          opt_flips_count++;
          return true;
        }
        // Due to 'unflip', the flip function may modify the sliver.
        if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) {
          // Try to flip the opposite edge of this sliver.
          edestoppo(bf->tt, sliver_edge); // face [c,d,a]
          if (flip_edge_to_improve(&sliver_edge, cosmaxd)) {
            opt_flips_count++;
            return true;
          }
        }
      } else if (shape == 'T') {
        triface sliver_edge;
        // flip_face_to_improve(...)
        edestoppo(bf->tt, sliver_edge); // face [c,d,a]
        if (flip_edge_to_improve(&sliver_edge, cosmaxd)) {
          opt_flips_count++;
          return true;
        }
      }
    }
  } else if (eta > b->opt_max_edge_ratio) {
    // It is a skinny tet.
    // This tet contains a relatively short edge. Check if it can be collapsed.
    REAL Lmin = bf->cent[3];

    // Get the shortest edge of this tet.
    triface short_edge = bf->tt;
    int i;
    for (i = 0; i < 6; i++) {
      short_edge.ver = edge2ver[i];
      REAL dd = distance(org(short_edge), dest(short_edge));
      //if (fabs(Lmin - dd) < 1e-8) break;
      if ((fabs(Lmin - dd) / Lmin) < 1e-4) break;
    }
    if (i == 6) {
      terminatetetgen(this, 2);
    }


    if (Lmin <= minedgelength) {
      // A very short edge. Check if it was correctly created.
      point e1 = org(short_edge);
      point e2 = dest(short_edge);
      if (issteinerpoint(e1)) {
        if (!create_a_shorter_edge(e1, e2)) {
          terminatetetgen(this, 2);
        }
      } else if (issteinerpoint(e2)) {
        if (!create_a_shorter_edge(e2, e1)) {
          terminatetetgen(this, 2);
        }
      }
    }

  } else {
    // It is neither a flat nor skinny tet. While it has a large asp.

  }


  if (bSteiners &&
      ((bf->key > opt_max_sliver_asp_ratio) || (cosmaxd < cosslidihed))) {
    // This sliver is not removed. Due to 'unflip', the flip function may
    //   modify the sliver.
    if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) {
      if (add_steinerpt_to_repair(bf, bSmooth)) {
        return true;
      }
    }
  } // if (bSteiners)

  return false; // not repaired
}

//============================================================================//
//                                                                            //
// repair_badqual_tets()    Repair all queued bad quality tet.                //
//                                                                            //
//============================================================================//

long tetgenmesh::repair_badqual_tets(bool bFlips, bool bSmooth, bool bSteiners)
{
  if (b->verbose > 1) {
    printf("  Repairing %ld bad quality tets.\n", badqual_tets_pool->items);
  }
  long repaired_count = 0l;

  while (badqual_tets_pool->items > 0) {
  
    // Get a badtet of highest priority.
    badface *bt = top_badtet();

    if (get_tet(bt->forg, bt->fdest, bt->fapex, bt->foppo, &(bt->tt))) {
      if (repair_tet(bt, bFlips, bSmooth, bSteiners)) {
        repaired_count++;
      } else {
        // Failed to repair this tet. Save it.
        badface *bf = NULL;
        unsplit_badtets->newindex((void **) &bf);
        *bf = *bt;
      }
    } // if (get_tet(...))

    // Return the badtet to the pool.
    dequeue_badtet();
  } // while (badqual_tets_pool->items > 0)

  if (unsplit_badtets->objects > 0l) {
    // Re-initialise the priority queue
    for (int i = 0; i < 64; i++) {
      bt_queuefront[i] = bt_queuetail[i] = NULL;
    }
    bt_firstnonemptyq = -1;
    bt_recentq = -1;
        
    for (int i = 0; i < unsplit_badtets->objects; i++) {
      badface *bt = (badface *) fastlookup(unsplit_badtets, i);
      enqueue_badtet(bt);
    }
    unsplit_badtets->restart();
  } // if (unsplit_badtets->objects > 0l)

  return repaired_count;
}

//============================================================================//
//                                                                            //
// improve_mesh()    Mesh improvement.                                        //
//                                                                            //
//============================================================================//

void tetgenmesh::improve_mesh()
{
  if (!b->quiet) {
    printf("Improving mesh...\n");
  }

  if (b->verbose) {
    printf("  Target maximum aspect ratio = %g.\n", b->opt_max_asp_ratio);
    printf("  Target maximum dihedral angle = %g.\n", b->optmaxdihedral);
    printf("  Maximum flip level   = %d.\n", b->opt_max_flip_level); // -O#
    printf("  Number of iterations = %d.\n", b->opt_iterations); // -O///#
  }

  long blt = b->tetrahedraperblock;
  badqual_tets_pool = new memorypool(sizeof(badface), blt, sizeof(void *), 0);
  badtetrahedrons = new memorypool(sizeof(triface), blt, sizeof(void *), 0);
  unsplit_badtets = new arraypool(sizeof(badface), 10);

  for (int i = 0; i < 64; i++) {
    bt_queuefront[i] = NULL;
  }
  bt_firstnonemptyq = -1;
  bt_recentq = -1;

  cos_large_dihed = cos(135. / 180. * PI); // used in get_tetqual

  cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); // set by -o/#

  // The smallest dihedral angle to identify slivers.
  REAL sliver_ang_tol = b->optmaxdihedral - 5.0;
  if (sliver_ang_tol < 172.0) {
    sliver_ang_tol = 172.;
  }
  cossmtdihed = cos(sliver_ang_tol / 180.0 * PI);

  // The smallest dihedral angle to split slivers.
  REAL split_sliver_ang_tol = b->optmaxdihedral + 10.0;
  if (split_sliver_ang_tol < 179.0) {
    split_sliver_ang_tol = 179.0;
  } else if (split_sliver_ang_tol > 180.0) {
    split_sliver_ang_tol = 179.9;
  }
  cosslidihed = cos(split_sliver_ang_tol / 180.0 * PI);

  opt_max_sliver_asp_ratio = b->opt_max_asp_ratio * 10.; // set by -o//#

  int attrnum = numelemattrib - 1;
  triface checktet; badface bf;

  // Put all bad tetrahedra into array.
  tetrahedrons->traversalinit();
  checktet.tet = tetrahedrontraverse();
  while (checktet.tet != NULL) {
    if (b->convex) { // -c
      // Skip this tet if it lies in the exterior.
      if (elemattribute(checktet.tet, attrnum) == -1.0) {
        checktet.tet = tetrahedrontraverse();
        continue;
      }
    }
    if (get_tetqual(&checktet, NULL, &bf)) {
      if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) {
        bf.forg  =  org(bf.tt);
        bf.fdest = dest(bf.tt);
        bf.fapex = apex(bf.tt);
        bf.foppo = oppo(bf.tt);
        enqueue_badtet(&bf);
      }
    } else {
      terminatetetgen(this, 2); // a degenerated tet.
    }
    checktet.tet = tetrahedrontraverse();
  }

  // Backup flip edge options.
  int bakautofliplinklevel = autofliplinklevel;
  int bakfliplinklevel = b->fliplinklevel;
  int bakmaxflipstarsize = b->flipstarsize;

  b->fliplinklevel = 1; // initial (<= b->opt_max_flip_level, -O#)
  b->flipstarsize = 10;  // b->optmaxflipstarsize;

  long total_repaired_count = 0l;
  long bak_pt_count = points->items;

  // Only using flips.
  while (badqual_tets_pool->items > 0) {
    long repaired_count = repair_badqual_tets(true, false, false);
    total_repaired_count += repaired_count;
    if (b->fliplinklevel < b->opt_max_flip_level) {
      b->fliplinklevel++;
    } else {
      break; // maximal flip level is reached.
    }
  } // while (badqual_tets_pool->items > 0)
  
  if (b->verbose > 1) {
    printf("  Repaired %ld tetrahedra by flips.\n", total_repaired_count);
    printf("  %ld badqual tets remained.\n", badqual_tets_pool->items);
  }

  int iter = 0;
  long bak_st_count = st_volref_count;
  while ((badqual_tets_pool->items > 0) && (iter < b->opt_iterations)) {
    //b->fliplinklevel++;
    long repaired_count = repair_badqual_tets(true, true, true);
    // Break if no repair and no new Steiner point.
    if ((repaired_count == 0l) && (bak_st_count == st_volref_count)) {
      break;
    }
    total_repaired_count += repaired_count;
    bak_st_count = st_volref_count;
    iter++;
  } // while (badqual_tets_pool->items > 0)

  // Do last flips.
  if (badqual_tets_pool->items > 0) {
    long repaired_count = repair_badqual_tets(true, false, false);
    total_repaired_count += repaired_count;
  }

  if (b->verbose > 1) {
    printf("  Repaired %ld tetrahedra.\n", total_repaired_count);
    printf("  %ld badqual tets remained.\n", badqual_tets_pool->items);
  }

  if (later_unflip_queue->objects > b->unflip_queue_limit) {
    //recoverdelaunay();
    later_unflip_queue->restart(); // clean it.
  }

  if (b->verbose) {
    if (opt_flips_count > 0l) {
      printf("  Removed %ld edges/faces.\n", opt_flips_count);
    }
    if (opt_collapse_count > 0l) {
      printf("  Collapsed %ld edges/faces.\n", opt_collapse_count);
    }
    if (opt_smooth_count > 0l) {
      printf("  Smoothed %ld vertices.\n", opt_smooth_count);
    }
    if ((points->items - bak_pt_count) > 0l) {
      printf("  Added %ld Steiner points.\n", points->items - bak_pt_count);
    }
  }


  // Restore original flip edge options.
  autofliplinklevel = bakautofliplinklevel;
  b->fliplinklevel = bakfliplinklevel;
  b->flipstarsize = bakmaxflipstarsize;

  delete badtetrahedrons;
  badtetrahedrons = NULL;
  delete badqual_tets_pool;
  badqual_tets_pool = NULL;
  delete unsplit_badtets;
  unsplit_badtets = NULL;
}

//                                                                            //
//                                                                            //
//== 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::check_mesh(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 *) NULL) {
    // 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] == NULL) {
        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);
        if (neightet.tet != NULL) {
          // 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++;
          }
        } else {
          printf("  !! !! Tet-face has no neighbor (%d, %d, %d) - %d:\n",
                 pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd));
          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::check_shells()
{
  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 != NULL) {
    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 != NULL) && (nextsh.sh != shloop.sh)) {
        if (nextsh.sh[3] == NULL) {
          printf("  !! !! Wrong subface-subface connection (Dead subface).\n");
          printf("    First: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                 pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                 pointmark(sapex(spinsh)));
          printf("    Second: x%lu (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%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                  pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                  pointmark(sapex(spinsh)));
           printf("    Scond: x%lu (%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%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                  pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                  pointmark(sapex(spinsh)));
           printf("    Scond: x%lu (%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 != NULL) {
        if (checkseg.sh[3] == NULL) {
          printf("  !! !! Wrong subface-subseg connection (Dead subseg).\n");
          printf("    Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                 pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                 pointmark(sapex(shloop)));
          printf("    Sub: x%lu (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%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                   pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                   pointmark(sapex(shloop)));
            printf("    Seg: x%lu (%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 != NULL) {
      if (neightet.tet[4] == NULL) {
        printf("  !! !! Wrong sub-to-tet connection (Dead tet)\n");
        printf("    Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh,
               pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
               pointmark(sapex(shloop)));
        printf("    Tet: x%lu (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%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh,
                 pointmark(sorg(shloop)), pointmark(sdest(shloop)), 
                 pointmark(sapex(shloop)));
          printf("    Tet: x%lu (%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%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                 pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                 pointmark(sapex(spinsh)));
          printf("    Tet: x%lu (%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 != NULL) {
          if (!((sorg(spinsh) == org(symtet)) && 
                (sdest(spinsh) == dest(symtet)))) {
            printf("  !! !! Wrong tet-sub connection.\n");
            printf("    Sub: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh,
                   pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), 
                   pointmark(sapex(spinsh)));
            printf("    Tet: x%lu (%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::check_segments()
{
  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 != NULL) {
    // Loop the six edges of the tet.
    if (tetloop.tet[8] != NULL) {
      segs = (shellface *) tetloop.tet[8];
      for (i = 0; i < 6; i++) {
        sdecode(segs[i], sseg);
        if (sseg.sh != NULL) {
          // 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%lu (%d, %d, %d, %d) - Seg: x%lu (%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%lu (%d, %d, %d, %d) - ",
                       (uintptr_t) neightet.tet, pointmark(org(neightet)),
                       pointmark(dest(neightet)), pointmark(apex(neightet)),
                       pointmark(oppo(neightet)));
                if (checkseg.sh != NULL) {
                  printf("Seg x%lu (%d, %d).\n", (uintptr_t) checkseg.sh,
                         pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); 
                } else {
                  printf("Seg: NULL.\n");
                }
                horrors++;
              }
              fnextself(neightet);
            } while (neightet.tet != tetloop.tet);
          }
          // Check the seg->tet pointer.
          sstpivot1(sseg, neightet);
          if (neightet.tet == NULL) {
            printf("  !! Wrong seg->tet connection (A NULL 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%lu (%d, %d, %d, %d) - Seg: x%lu (%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%lu %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%lu %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 != NULL) {
    pa = sorg(sseg);
    pb = sdest(sseg);
    spivot(sseg, neighsh);
    if (neighsh.sh != NULL) {      
      spinsh = neighsh;
      while (1) {
        // Check seg-subface bond.
        point e1 = sorg(spinsh);
        point e2 = sdest(spinsh);
        if (((e1 == pa) && (e2 == pb)) ||
	        ((e1 == pb) && (e2 == pa))) {
          // Keep the same rotate direction.
          //if (sorg(spinsh) != pa) {          
          //  sesymself(spinsh);
          //  printf("  !! Wrong ori at subface (%d, %d, %d) -- x%lu %d\n",
          //         pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
          //         pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh,
          //         spinsh.shver);
          //  horrors++;
          //}
          stpivot(spinsh, spintet);
          if (spintet.tet != NULL) {
            // Check if all tets at this segment.
            while (1) {
              tsspivot1(spintet, checkseg);
              if (checkseg.sh == NULL) {
                printf("  !! !! No seg at tet (%d, %d, %d, %d) -- x%lu %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 != NULL) break;
            } // while (1)
          }
        } else {
          point e3 = sapex(spinsh);
          printf("  !! Wrong seg-subface (%d, %d) - (%d, %d, %d) connect\n",
                 pointmark(pa), pointmark(pb),
                 (e1 != NULL ? pointmark(e1) : -1),
                 (e2 != NULL ? pointmark(e2) : -1),
                 (e3 != NULL ? pointmark(e3) : -1)
                 //(uintptr_t) spinsh.sh,
                 //spinsh.shver
                 );
          horrors++;
          break;
        } // if pa, pb
        spivotself(spinsh);
        if (spinsh.sh == NULL) break; // A dangling segment.
        if (spinsh.sh == neighsh.sh) break;
      } // while (1)
    } // if (neighsh.sh != NULL)
    // Count the number of "un-recovered" segments.
    sstpivot1(sseg, neightet);
    if (neightet.tet == NULL) {
      miscount++;
    }
    sseg.sh = shellfacetraverse(subsegs);
  }

  if (!b->quiet) {
    printf("  Checking seg->seg connections...\n");
  }

  points->traversalinit();
  pa = pointtraverse();
  while (pa != NULL) {
    if (pointtype(pa) == FREESEGVERTEX) {
      // There should be two subsegments connected at 'pa'.
      // Get a subsegment containing 'pa'.
      sdecode(point2sh(pa), sseg);
      if ((sseg.sh == NULL) || sseg.sh[3] == NULL) {
        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 == NULL) || (checkseg.sh[3] == NULL)) {
              printf("  !! Dead seg-seg connection at point %d.\n",
                     pointmark(pa));
              horrors++;
            } else {
              spivotself(checkseg);
              checkseg.shver = 0;
              if ((sorg(checkseg) != pa) && (sdest(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 == NULL) || (checkseg.sh[3] == NULL)) {
            printf("  !! Dead seg-seg connection at point %d.\n",
                   pointmark(pa));
            horrors++;
          } else {
            spivotself(checkseg);
            checkseg.shver = 0;
            if ((sorg(checkseg) != pa) && (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::check_delaunay(int perturb)
{
  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 *) NULL) {
    // 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);
        if (perturb) {
          sign = insphere_s(pa, pb, pc, pd, pe);
        } else {
          sign = insphere(pa, pb, pc, pd, pe);
        }
        if (sign < 0.0) {
          ndcount++;
          if (checksubfaceflag) {
            tspivot(tetloop, checksh);
          }
          if (checksh.sh == NULL) {
            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::check_regular(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 *) NULL) {
    // 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 == NULL) {
            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::check_conforming(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 *) NULL) {
      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 != NULL) {
        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 *) NULL) {
      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 smallestradiusratio, biggestradiusratio; // radius-edge ratio.
  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;
  unsigned long radiustable[12];
  unsigned long aspecttable[16];
  unsigned long dihedangletable[18];
  unsigned long faceangletable[18];
  int indx[4];
  int radiusindex;
  int aspectindex;
  int tendegree;
  int i, j;
  // Report the tet which has the biggest radius-edge ratio.
  triface biggestradiusratiotet;
  // Report the tet which has the biggest volume.
  triface biggestvolumetet;
  triface longestedgetet;

  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] = 0l;
  for (i = 0; i < 12; i++) aspecttable[i] = 0l;
  for (i = 0; i < 18; i++) dihedangletable[i] = 0l;
  for (i = 0; i < 18; i++) faceangletable[i] = 0l;

  minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
  minaltitude = minaltitude * minaltitude;
  shortest = minaltitude;
  longest = 0.0;
  smallestvolume = minaltitude;
  biggestvolume = 0.0;
  smallestratio = smallestradiusratio = 1e+16; // minaltitude;
  biggestratio = biggestradiusratio = 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();
  
  //int tidx = 1;
  
  while (tetloop.tet != (tetrahedron *) NULL) {

    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;
      biggestvolumetet.tet = tetloop.tet;
    }

    // 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 lengths.
    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];
        longestedgetet.tet = tetloop.tet;
      } 
      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 < 4; i++) {
        if (H[i] > minheightinv) minheightinv = H[i];
      }
    } else {
      // A nearly degenerated tet.
      if (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, NULL);
      facenormal(p[0], p[2], p[3], N[1], 1, NULL);
      facenormal(p[1], p[0], p[3], N[2], 1, NULL);
      facenormal(p[0], p[1], p[2], N[3], 1, NULL);
      // 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 < 4; 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]++;
    }



    // Calculate 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], NULL);
        faceangle[1] = interiorangle(p[1], p[2], p[0], NULL);
        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);
    if (tetradius < smallestradiusratio) {
      smallestradiusratio = tetradius;
    }
    if (tetradius > biggestradiusratio) {
      biggestradiusratio = tetradius;
      biggestradiusratiotet.tet = tetloop.tet;
    }
    // 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    :  %8ld      | %6.6g - %-6.6g     :  %8ld\n",
         aspectratiotable[0], aspecttable[0], aspectratiotable[5],
         aspectratiotable[6], aspecttable[6]);
  for (i = 1; i < 5; i++) {
    printf("  %6.6g - %-6.6g    :  %8ld      | %6.6g - %-6.6g     :  %8ld\n",
           aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
           aspectratiotable[i + 5], aspectratiotable[i + 6],
           aspecttable[i + 6]);
  }
  printf("  %6.6g - %-6.6g    :  %8ld      | %6.6g -            :  %8ld\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:  %8ld      |    %3d - %3d degrees:  %8ld\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:  %8ld      |    %3d - %3d degrees:  %8ld\n",
         0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
  printf("     %3d - %2d degrees:  %8ld      |    %3d - %3d degrees:  %8ld\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:  %8ld      |    %3d - %3d degrees:  %8ld\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:  %8ld      |    %3d - %3d degrees:  %8ld\n",
         60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
  printf("     %3d - %2d degrees:  %8ld      |    %3d - %3d degrees:  %8ld\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 != NULL) {
    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 (in->numberoftrifaces > 0) {
	  printf("  Input triangles: %d\n", in->numberoftrifaces);
    }
    if (in->numberofedges > 0) {
	  printf("  Input edges: %d\n", in->numberofedges);
	}
  } else if (b->plc) {
    printf("  Input facets: %d\n", in->numberoffacets);
    printf("  Input segments: %ld\n", insegments);
	if (in->numberofedges > 0) {
	  printf("  Input edges: %d\n", in->numberofedges);
	}
    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 faces on exterior boundary: %ld\n", hullsize);
    if (meshhulledges > 0l) {
      printf("  Mesh edges on exterior boundary: %ld\n", meshhulledges);
    }
    printf("  Mesh faces on input facets: %ld\n", subfaces->items);
    printf("  Mesh edges on input segments: %ld\n", subsegs->items);
    if (st_facref_count > 0l) {
      printf("  Steiner points on input facets:  %ld\n", st_facref_count);
    }
    if (st_segref_count > 0l) {
      printf("  Steiner points on input segments:  %ld\n", st_segref_count);
    }
    if (st_volref_count > 0l) {
      printf("  Steiner points inside 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 redundant points.\n");
  }

  points->traversalinit();
  pointloop = pointtraverse();
  oldidx = newidx = 0; // in->firstnumber;
  remcount = 0;
  while (pointloop != (point) NULL) {
    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 *) NULL) {
        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 *) NULL;
}

//============================================================================//
//                                                                            //
// 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'.
  point *highordertable = new point[tetrahedrons->items * 6];
  if (highordertable == (point *) NULL) {
    terminatetetgen(this, 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 *) NULL;

  // Assign an entry for each tetrahedron to find its extra nodes. At the
  //   mean while, initialize all extra nodes be NULL.
  i = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i];
    for (j = 0; j < 6; j++) {
      highordertable[i + j] = (point) NULL;
    }
    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 NULL, 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 *) NULL) {
    // 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) NULL) {
        // 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();
  }

  delete [] highordertable;
}

//============================================================================//
//                                                                            //
// indexelements()    Index all tetrahedra.                                   //
//                                                                            //
// Many output functions require that the tetrahedra are indexed.  This       //
// routine is called when -E option is used.                                  //
//                                                                            //
//============================================================================//

void tetgenmesh::indexelements()
{
  triface worktet;
  int eindex = b->zeroindex ? 0 : in->firstnumber; // firstindex;
  tetrahedrons->traversalinit();
  worktet.tet = tetrahedrontraverse();
  while (worktet.tet != NULL) {
    setelemindex(worktet.tet, eindex);
    eindex++;
	if (b->metric) { // -m option
	  // Update the point-to-tet map, so that every point is pointing
	  //   to a real tet, not a fictious one. Used by .p2t file.
	  tetrahedron tptr = encode(worktet);
	  for (int i = 0; i < 4; i++) {
	    setpoint2tet((point) (worktet.tet[4 + i]), tptr);
	  }
	}
    worktet.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 != NULL) {
    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);
      if (spintet.tet == worktet.tet) {
        meshedges++;
        if (ishulledge) meshhulledges++;
      }
    }
    infect(worktet);
    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 = NULL;
  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 *) NULL) {
    strcpy(outnodefilename, b->outfilename);
    strcat(outnodefilename, ".node");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      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 *) NULL) {
    outfile = fopen(outnodefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outnodefilename);
      terminatetetgen(this, 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);
    // [2020-01-16] added save flag (for viewing Steiner points).
    //fprintf(outfile, "%ld  %d  %d  %d  1\n", points->items, 3, nextras, bmark);
    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 *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 1);
    }
    // Allocate space for 'pointattributelist' if necessary;
    if (nextras > 0) {
      out->pointattributelist = new REAL[points->items * nextras];
      if (out->pointattributelist == (REAL *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 1);
      }
    }
    // Allocate space for 'pointmarkerlist' if necessary;
    if (bmark) {
      out->pointmarkerlist = new int[points->items];
      if (out->pointmarkerlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 1);
      }
    }
    if (b->psc) {
      out->pointparamlist = new tetgenio::pointparam[points->items];
      if (out->pointparamlist == NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 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) NULL) {
    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 != NULL) {
            marker = shellmark(parentsh);
          }
        } // if (pointtype(...))
      }
    }
    if (out == (tetgenio *) NULL) {
      // 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.
        }
      }
      // // [2020-01-16] Write vertex flags
      // if (pointnumber > in->numberofpoints) {
      //   fprintf(outfile, " 16"); // A Steiner point.
      // } else {
      //   fprintf(outfile, " 0");
      // }
      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 *) NULL) {
    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 = NULL;
  char outmtrfilename[FILENAMESIZE];
  point ptloop;
  int mtrindex = 0;
  int i;
  int msize = (sizeoftensor - useinsertradius);
  if (msize == 0) {
    return;
  }

  if (out == (tetgenio *) NULL) {
    strcpy(outmtrfilename, b->outfilename);
    strcat(outmtrfilename, ".mtr");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", outmtrfilename);
    } else {
      printf("Writing metrics.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outmtrfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outmtrfilename);
      terminatetetgen(this, 3);
    }
    // Number of points, number of point metrices,
    fprintf(outfile, "%ld  %d\n", points->items, msize);
  } else {
    // Allocate space for 'pointmtrlist'.
    out->numberofpointmtrs = msize; 
    out->pointmtrlist = new REAL[points->items * msize];
    if (out->pointmtrlist == (REAL *) NULL) {
      terminatetetgen(this, 1);
    }
  }

  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    if (out == (tetgenio *) NULL) {
      for (i = 0; i < msize; i++) {
        fprintf(outfile, " %-16.8e", ptloop[pointmtrindex + i]);
      }
      fprintf(outfile, "\n");
    } else {
      for (i = 0; i < msize; i++) {
        out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
      }
    }
    ptloop = pointtraverse();
  }

  // Output the point-to-tet map.
  if (out == (tetgenio *) NULL) {
    strcpy(outmtrfilename, b->outfilename);
    strcat(outmtrfilename, ".p2t");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", outmtrfilename);
    } else {
      printf("Writing point-to-tet map.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outmtrfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outmtrfilename);
      terminatetetgen(this, 3);
    }
    // Number of points,
    //fprintf(outfile, "%ld\n", points->items);
  } else {
    // Allocate space for 'point2tetlist'.
    out->point2tetlist = new int[points->items];
    if (out->point2tetlist == (int *) NULL) {
      terminatetetgen(this, 1);
    }
  }

  // The list of tetrahedra must be indexed.
  if (bgm != NULL) {
    bgm->indexelements();
  }
  // Determine the first index (0 or 1).
  int firstindex = b->zeroindex ? 0 : in->firstnumber;
  int pointindex = firstindex;
  i = 0;

  triface parenttet;
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    if (bgm != NULL) {
	  bgm->decode(point2bgmtet(ptloop), parenttet);
	} else {
	  decode(point2tet(ptloop), parenttet);
	}
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%d  %d\n", pointindex, elemindex(parenttet.tet));
    } else {
      out->point2tetlist[i] = elemindex(parenttet.tet);
    }
	pointindex++;
	i++;
    ptloop = pointtraverse();
  }

  if (out == (tetgenio *) NULL) {
    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 = NULL;
  char outelefilename[FILENAMESIZE];
  tetrahedron* tptr;
  point p1, p2, p3, p4;
  point *extralist;
  REAL *talist = NULL;
  int *tlist = NULL;
  long ntets;
  int firstindex, shift;
  int pointindex, attribindex;
  int highorderindex = 11; 
  int elementnumber;
  int eextras;
  int i;

  if (out == (tetgenio *) NULL) {
    strcpy(outelefilename, b->outfilename);
    strcat(outelefilename, ".ele");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      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 *) NULL) {
    outfile = fopen(outelefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outelefilename);
      terminatetetgen(this, 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 *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 1);
    }
    // Allocate memory for output tetrahedron attributes if necessary.
    if (eextras > 0) {
      out->tetrahedronattributelist = new REAL[ntets * eextras];
      if (out->tetrahedronattributelist == (REAL *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 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 shift.
  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 *) NULL) {
    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 *) NULL) {
      // 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);
	if (b->metric) { // -m option
	  // Update the point-to-tet map, so that every point is pointing
	  //   to a real tet, not a fictious one. Used by .p2t file.
	  for (int i = 0; i < 4; i++) {
	    setpoint2tet((point) (tptr[4 + i]), (tetrahedron) tptr);
	  }
	}
    tptr = tetrahedrontraverse();
    elementnumber++;
  }


  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

//============================================================================//
//                                                                            //
// outfaces()    Output all faces to a .face file or a tetgenio object.       //
//                                                                            //
// The total number of faces f can be calculated as following:  Let t be the  //
// total number of tets. Since each tet has 4 faces, the number t * 4 counts  //
// each interior face twice and each hull face once. So f = (t * 4 + h) / 2,  //
// where h is the total number of hull faces (which is known).                //
//                                                                            //
//============================================================================//

void tetgenmesh::outfaces(tetgenio* out)
{
  FILE *outfile = NULL;
  char facefilename[FILENAMESIZE];
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  long ntets, faces;
  int *elist = NULL, *emlist = NULL;
  int neigh1 = 0, neigh2 = 0;
  int 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;

  // For -nn option.
  int *tet2facelist = NULL;
  int tidx; 

  if (out == (tetgenio *) NULL) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  ntets = tetrahedrons->items - hullsize;
  faces = (ntets * 4l + hullsize) / 2l;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(this, 1);
    }
    fprintf(outfile, "%ld  %d\n", faces, !b->nobound);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[faces * 3];
    if (out->trifacelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 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 *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->face2tetlist = new int[faces * 2];
      if (out->face2tetlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(this, 1);
      }
    }
    out->numberoftrifaces = faces;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
  }

  if (b->neighout > 1) { // -nn option
    // Output the tetrahedron-to-face map.
    tet2facelist = new int[ntets * 4];
  }

  // 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 *) NULL) {
    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 == NULL) {
              marker = 0;  // It is an inner face. It's marker is 0.
            } else {
              marker = shellmark(checkmark);
            }
          } 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.
          if (!ishulltet(tface)) {
            neigh1 = elemindex(tface.tet);
          } else {
            neigh1 = -1;
          }
          if (!ishulltet(tsymface)) {
            neigh2 = elemindex(tsymface.tet);
          } else {
            neigh2 = -1;  
          }
		  // Fill the tetrahedron-to-face map.
		  tidx = elemindex(tface.tet) - firstindex;
		  tet2facelist[tidx * 4 + tface.ver] = facenumber;
		  if (!ishulltet(tsymface)) {
		    tidx = elemindex(tsymface.tet) - firstindex;
			tet2facelist[tidx * 4 + (tsymface.ver & 3)] = facenumber;
		  }
        }
        if (out == (tetgenio *) NULL) {
          // 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->face2tetlist[(facenumber - in->firstnumber) * 2]     = neigh1;
            out->face2tetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
          }
        }
        facenumber++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  if (b->neighout > 1) { // -nn option
    // Output the tetrahedron-to-face map.
	if (out == (tetgenio *) NULL) {
	  strcpy(facefilename, b->outfilename);
      strcat(facefilename, ".t2f");
    }
	if (!b->quiet) {
      if (out == (tetgenio *) NULL) {
        printf("Writing %s.\n", facefilename);
      } else {
        printf("Writing tetrahedron-to-face map.\n");
      }
    }
	if (out == (tetgenio *) NULL) {
      outfile = fopen(facefilename, "w");
      for (tidx = 0; tidx < ntets; tidx++) {
        index = tidx * 4;
        fprintf(outfile, "%4d  %d %d %d %d\n", tidx + in->firstnumber,
                tet2facelist[index], tet2facelist[index+1], 
                tet2facelist[index+2], tet2facelist[index+3]);
      }
      fclose(outfile);
      delete [] tet2facelist;
    } else {
	  // Simply copy the address of the list to the output.
      out->tet2facelist = tet2facelist;
    }
  }
}

//============================================================================//
//                                                                            //
// 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 = NULL;
  char facefilename[FILENAMESIZE];
  triface hulltet;
  point torg, tdest, tapex;
  int *elist = NULL;
  int firstindex, shift;
  int facenumber;
  int index;

  if (out == (tetgenio *) NULL) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(this, 1);
    }
    fprintf(outfile, "%ld  0\n", hullsize);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[hullsize * 3];
    if (out->trifacelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 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 *) NULL) {
    if (ishulltet(hulltet)) {
      torg = (point) hulltet.tet[4];
      tdest = (point) hulltet.tet[5];
      tapex = (point) hulltet.tet[6];
      if (out == (tetgenio *) NULL) {
        // 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 *) NULL) {
    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 = NULL;
  char facefilename[FILENAMESIZE];
  int *elist = NULL;
  int *emlist = NULL;
  int index = 0, index1 = 0, index2 = 0;
  triface abuttingtet;
  face faceloop;
  point torg, tdest, tapex;
  int 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 *) NULL) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", facefilename);
    } else {
      printf("Writing faces.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(this, 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 *) NULL) {
      terminatetetgen(this, 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 *) NULL) {
        terminatetetgen(this, 1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->face2tetlist = new int[subfaces->items * 2];
      if (out->face2tetlist == (int *) NULL) {
        terminatetetgen(this, 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 *) NULL) {
    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 != NULL) {
      if (ishulltet(abuttingtet)) {
        fsymself(abuttingtet);
      }
    }
    if (abuttingtet.tet != NULL) {
      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) {
      marker = shellmark(faceloop);
    }
    if (b->neighout > 1) {
      // '-nn' switch. Output adjacent tets indices.
      neigh1 = -1;
      neigh2 = -1;
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != NULL) {
        if (!ishulltet(abuttingtet)) {
          neigh1 = elemindex(abuttingtet.tet);
        }
        fsymself(abuttingtet);
        if (!ishulltet(abuttingtet)) {
          neigh2 = elemindex(abuttingtet.tet);
        }
      }
    }
    if (out == (tetgenio *) NULL) {
      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->face2tetlist[index2++] = neigh1;
        out->face2tetlist[index2++] = neigh2;
      }
    }
    facenumber++;
    faceloop.sh = shellfacetraverse(subfaces);
  }

  if (out == (tetgenio *) NULL) {
    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 = NULL;
  char edgefilename[FILENAMESIZE];
  triface tetloop, worktet, spintet;
  face checkseg;
  point torg, tdest;
  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 = NULL; 
  int highorderindex = 11;
  int o2index = 0;

  // For -nn option.
  int *tet2edgelist = NULL;
  int tidx;

  if (out == (tetgenio *) NULL) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      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;
    }
  }
  meshhulledges = 0l; // It will be counted.

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(this, 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->numberofedges = meshedges;
    out->edgelist = new int[meshedges * 2];
    if (out->edgelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 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->edge2tetlist = new int[meshedges];
    }
  }

  if (b->neighout > 1) { // -nn option
    // Output the tetrahedron-to-edge map.
	long tsize = tetrahedrons->items - hullsize;
    tet2edgelist = new int[tsize * 6];
  }

  // 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 *) NULL) {
    // 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);
      if (spintet.tet == worktet.tet) {
        // Found a new edge.
        if (ishulledge) meshhulledges++;
        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 *) NULL) {
          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;
          out->edgelist[index++] = pointmark(torg) - shift;
          out->edgelist[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 != NULL) {
              marker = shellmark(checkseg);
            } else {
              marker = 0;  // It's not a segment.
            }
          } else {
            // Mark it if it is a hull edge.
            marker = ishulledge ? 1 : 0;
          }
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, "  %d", marker);
          } else {
            out->edgemarkerlist[index1++] = marker;
          }
        }
        if (b->neighout > 1) { // '-nn' switch.
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, "  %d", elemindex(tetloop.tet));
          } else {
            out->edge2tetlist[index2++] = elemindex(tetloop.tet);
          }
		  // Fill the tetrahedron-to-edge map.
		  spintet = worktet;
		  while (1) {
		    if (!ishulltet(spintet)) {
			  tidx = elemindex(spintet.tet) - firstindex;
			  tet2edgelist[tidx * 6 + ver2edge[spintet.ver]] = edgenumber;
			}
		    fnextself(spintet);
			if (spintet.tet == worktet.tet) break;
		  }
        }
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "\n");
        }
        edgenumber++;
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  if (b->neighout > 1) { // -nn option
    long tsize = tetrahedrons->items - hullsize;

    if (b->facesout) { // -f option
      // Build the face-to-edge map (use the tet-to-edge map).
	  long fsize = (tsize * 4l + hullsize) / 2l;
	  int *face2edgelist = new int[fsize * 3];

	  tetrahedrons->traversalinit();
      tetloop.tet = tetrahedrontraverse();
      int facenumber = 0; // firstindex; // in->firstnumber;
	  while (tetloop.tet != (tetrahedron *) NULL) {
	    for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
		  fsym(tetloop, spintet);
		  if (ishulltet(spintet) || 
              (elemindex(tetloop.tet) < elemindex(spintet.tet))) {
            // The three edges of this face are ordered such that the
			//    first edge is opposite to the first vertex of this face
			//    that appears in the .face file, and so on.
			tidx = elemindex(tetloop.tet) - firstindex; 
			worktet = tetloop;
            for (i = 0; i < 3; i++) {
			  enextself(worktet); // The edge opposite to vertex i.
			  int eidx = tet2edgelist[tidx * 6 + ver2edge[worktet.ver]];
			  face2edgelist[facenumber * 3 + i] = eidx;
			}
		    facenumber++;
		  }
		}
	    tetloop.tet = tetrahedrontraverse();
	  }

	  // Output the face-to-edge map.
	  if (out == (tetgenio *) NULL) {
	    strcpy(edgefilename, b->outfilename);
        strcat(edgefilename, ".f2e");
      }
	  if (!b->quiet) {
        if (out == (tetgenio *) NULL) {
          printf("Writing %s.\n", edgefilename);
        } else {
          printf("Writing face-to-edge map.\n");
        }
      }
	  if (out == (tetgenio *) NULL) {
	    outfile = fopen(edgefilename, "w");
	    for (tidx = 0; tidx < fsize; tidx++) { // Re-use `tidx'
	      i = tidx * 3;
		  fprintf(outfile, "%4d  %d %d %d\n", tidx + in->firstnumber,
		          face2edgelist[i], face2edgelist[i+1], face2edgelist[i+2]);
	    }
	    fclose(outfile);
	    delete [] face2edgelist;
	  } else {
	    // Simply copy the address of the list to the output.
	    out->face2edgelist = face2edgelist;
      }
    } // if (b->facesout)

    // Output the tetrahedron-to-edge map.
	if (out == (tetgenio *) NULL) {
	  strcpy(edgefilename, b->outfilename);
      strcat(edgefilename, ".t2e");
    }
	if (!b->quiet) {
      if (out == (tetgenio *) NULL) {
        printf("Writing %s.\n", edgefilename);
      } else {
        printf("Writing tetrahedron-to-edge map.\n");
      }
    }
	if (out == (tetgenio *) NULL) {
	  outfile = fopen(edgefilename, "w");
	  for (tidx = 0; tidx < tsize; tidx++) {
	    i = tidx * 6;
		fprintf(outfile, "%4d  %d %d %d %d %d %d\n", tidx + in->firstnumber,
		        tet2edgelist[i], tet2edgelist[i+1], tet2edgelist[i+2],  
				tet2edgelist[i+3], tet2edgelist[i+4], tet2edgelist[i+5]);
	  }
	  fclose(outfile);
	  delete [] tet2edgelist;
	} else {
	  // Simply copy the address of the list to the output.
	  out->tet2edgelist = tet2edgelist;
    }
  }
}

//============================================================================//
//                                                                            //
// outsubsegments()    Output segments to a .edge file or a structure.        //
//                                                                            //
//============================================================================//

void tetgenmesh::outsubsegments(tetgenio* out)
{
  FILE *outfile = NULL;
  char edgefilename[FILENAMESIZE];
  int *elist = NULL;
  int index, i;
  face edgeloop;
  point torg, tdest;
  int firstindex, shift;
  int marker;
  int edgenumber;

  // For -o2 option.
  triface workface, spintet;
  point *extralist, pp = NULL; 
  int highorderindex = 11;
  int o2index = 0;

  // For -nn option.
  int neigh = -1;
  int index2 = 0;

  int t1ver; // used by fsymself()

  if (out == (tetgenio *) NULL) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", edgefilename);
    } else {
      printf("Writing edges.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(this, 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 *) NULL) {
      terminatetetgen(this, 1);
    }
    if (b->order == 2) {
      out->o2edgelist = new int[subsegs->items];
    }
    out->edgemarkerlist = new int[subsegs->items];
    if (out->edgemarkerlist == (int *) NULL) {
      terminatetetgen(this, 1);
    }
    if (b->neighout > 1) {
      out->edge2tetlist = 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 *) NULL) {
    torg = sorg(edgeloop);
    tdest = sdest(edgeloop);
    if ((b->order == 2) || (b->neighout > 1)) {
      sstpivot1(edgeloop, workface);
      if (workface.tet != NULL) {
        // 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;
          }
          workface = spintet;
        }
      }
    }
    if (b->order == 2) { // -o2
      // Get the extra vertex on this edge.
      if (workface.tet != NULL) {
        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 != NULL) {
        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 *) NULL) {
      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->edge2tetlist[index2++] = neigh;
      }
    }
    edgenumber++;
    edgeloop.sh = shellfacetraverse(subsegs);
  }

  if (out == (tetgenio *) NULL) {
    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 = NULL;
  char neighborfilename[FILENAMESIZE];
  int *nlist = NULL;
  int index = 0;
  triface tetloop, tetsym;
  int neighbori[4];
  int firstindex;
  int elementnumber;
  long ntets;

  if (out == (tetgenio *) NULL) {
    strcpy(neighborfilename, b->outfilename);
    strcat(neighborfilename, ".neigh");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", neighborfilename);
    } else {
      printf("Writing neighbors.\n");
    }
  }

  ntets = tetrahedrons->items - hullsize;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(neighborfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", neighborfilename);
      terminatetetgen(this, 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 *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(this, 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 *) NULL) {
    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 *) NULL) {
      // 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 *) NULL) {
    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 internal 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.                                       //
//                                                                            //
// 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 = NULL;
  char outfilename[FILENAMESIZE];
  tetgenio::voroedge *vedge = NULL;
  tetgenio::vorofacet *vfacet = NULL;
  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 = NULL;
  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 *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.node");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      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 != NULL) {
    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;
  if (!nonconvex) {
    edges = vsize + faces - ntets - 1;
  } else {
    if (meshedges == 0l) {
      numberedges(); // Count edges.
    }
    edges = meshedges; 
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(this, 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 *) NULL) {
      terminatetetgen(this, 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 *) NULL) {
    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, NULL);
    } else {
      circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL);
    }
    if (out == (tetgenio *) NULL) {
      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 *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi edges to .v.edge file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.edge");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi edges.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(this, 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 *) NULL) {
    // 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 *) NULL) {
          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 *) NULL) {
            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 *) NULL) {
            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 *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi faces to .v.face file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.face");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi faces.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(this, 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 *) NULL) {
      terminatetetgen(this, 1);
    }
  }

  // Output the Voronoi facets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vfacecount = 0; // D-edge (V-facet) index (from zero).
  while (tetloop.tet != (tetrahedron *) NULL) {
    // 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 *) NULL) {
          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 *) NULL) {
            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 *) NULL) {
          fprintf(outfile, "\n");
        }
        vfacecount++;
      } // if (spintet.tet == worktet.tet)
    } // if (i = 0; i < 6; i++)
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi cells to .v.cell file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.cell");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", outfilename);
    } else {
      printf("Writing Voronoi cells.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(this, 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 **) NULL) {
      terminatetetgen(this, 1);
    }
  }

  // Output Voronoi cells.
  tetlist = cavetetlist;
  ptlist = cavetetvertlist;
  points->traversalinit();
  ploop = pointtraverse();
  vpointcount = 0;
  while (ploop != (point) NULL) {
    if ((pointtype(ploop) != UNUSEDVERTEX) &&
        (pointtype(ploop) != DUPLICATEDVERTEX) &&
        (pointtype(ploop) != NREGULARVERTEX)) { 
      getvertexstar(1, ploop, tetlist, ptlist, NULL);
      // 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 *) NULL) {
        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 *) NULL) {
                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 *) NULL) {
          fprintf(outfile, " -1");
        } else {
          vertarray[index++] = -1;
        }
      }
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "\n");
      }
      tetlist->restart();
      ptlist->restart();
      vpointcount++;
    }
    ploop = pointtraverse();
  }

  // Delete the space for face/edge indices.
  delete [] indexarray;

  if (out == (tetgenio *) NULL) {
    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 NULL), 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 marker;
  int i;

  if (smfilename != (char *) NULL && 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 *) NULL) {
    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 || in->trifacemarkerlist);  

  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 *) NULL) {
    p1 = sorg(faceloop);
    p2 = sdest(faceloop);
    p3 = sapex(faceloop);
    if (bmark) {
      marker = shellmark(faceloop);
    }
    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 NULL), 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 faceloop, segloop, checkmark;
  point ptloop, p1, p2, p3, p4;
  long ntets, faces;
  int pointnumber;
  int marker;
  int i;

  if (mfilename != (char *) NULL && mfilename[0] != '\0') {
    strcpy(mefilename, mfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(mefilename, b->outfilename);
  } else {
    strcpy(mefilename, "unnamed");
  }
  strcat(mefilename, ".mesh");

  int *subdomains_facets = NULL;
  int *subdomains_facets_ori = NULL;
  int sub_count = 0; // Count the number of indexed subdomains.
  if (subdomains > 0) {
    subdomains_facets = new int[subdomains];
    subdomains_facets_ori = new int[subdomains];
    for (i = 0; i < subdomains; i++) {
      subdomains_facets_ori[i] = 0; // initialise
    }
  }

  if (!b->quiet) {
    printf("Writing %s.\n", mefilename);
  }
  outfile = fopen(mefilename, "w");
  if (outfile == (FILE *) NULL) {
    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) NULL) {
    // 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++;
  }

  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 *) NULL) {
      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);
    }
  }

  ntets = tetrahedrons->items - hullsize;

  faces = subfaces->items;
  triface abuttingtet;
  int t1ver;

  fprintf(outfile, "\n# Set of Triangles\n");
  fprintf(outfile, "Triangles\n");
  fprintf(outfile, "%ld\n", faces);
  
  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  int facidx = 1; // Index facets for subdomains.
  while (faceloop.sh != (shellface *) NULL) {
    stpivot(faceloop, abuttingtet);
    if (abuttingtet.tet != NULL) {
      if (ishulltet(abuttingtet)) {
        fsymself(abuttingtet);
      }
    }
    if (abuttingtet.tet != NULL) {
      p1 = org (abuttingtet);
      p2 = dest(abuttingtet);
      p3 = apex(abuttingtet); 
      if (subdomains) {
        int attr = elemattribute(abuttingtet.tet, 0);
        int idx = attr - 1; 
        if (subdomain_markers[idx] != attr) {
          // search it.
        }
        if (subdomains_facets_ori[idx] == 0) {
          subdomains_facets[idx] = facidx;
          subdomains_facets_ori[idx] = 1;
          sub_count++;
          fsym(abuttingtet, tsymface);
          if ((tsymface.tet != NULL) && !ishulltet(tsymface)) {
            attr = elemattribute(tsymface.tet, 0);
            idx = attr - 1; 
            if (subdomain_markers[idx] != attr) {
              // search it.
            }
            if (subdomains_facets_ori[idx] == 0) {
              subdomains_facets[idx] = facidx;
              subdomains_facets_ori[idx] = -1;
              sub_count++;
            }            
          }                  
        }
      }
    } else {
      // A dangling subfacet.
      p1 = sorg(faceloop);
      p2 = sdest(faceloop);
      p3 = sapex(faceloop); 
    } 
    marker = shellmark(faceloop); 
    fprintf(outfile, "%5d  %5d  %5d    %d\n",
                pointmark(p1), pointmark(p2), pointmark(p3), marker);    
    //setelemindex(faceloop.sh, facidx);
    facidx++;   
    faceloop.sh = shellfacetraverse(subfaces);
  }  


  fprintf(outfile, "\n# Set of Tetrahedra\n");
  fprintf(outfile, "Tetrahedra\n");
  fprintf(outfile, "%ld\n", ntets);

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  while (tetptr != (tetrahedron *) NULL) {
    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 (subdomains > 0) {
    fprintf(outfile, "\nSubDomainFromGeom\n");
    fprintf(outfile, "%d\n", subdomains);    
    for (i = 0; i < subdomains; i++) {
      fprintf(outfile, "3  %d  %d  %d\n", 
              subdomains_facets[i],
              subdomains_facets_ori[i], 
              subdomain_markers[i]);
    }    
    delete [] subdomains_facets;
    delete [] subdomains_facets_ori;
  }

  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, int mesh_idx)
{
  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;

  if (b->order == 2) {
    printf("  Write VTK not implemented for order 2 elements \n");
    return;
  }

  int NEL = tetrahedrons->items - hullsize;
  int NN = points->items;

  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
    //strcpy(vtkfilename, ofilename);
    sprintf(vtkfilename, "%s.%d.vtk", ofilename, mesh_idx);
  } else if (b->outfilename[0] != '\0') {
    strcpy(vtkfilename, b->outfilename);
    strcat(vtkfilename, ".vtk");
  } else {
    strcpy(vtkfilename, "noname.vtk");
  }

  if (!b->quiet) {
    printf("Writing %s.\n", vtkfilename);
  }
  outfile = fopen(vtkfilename, "w");
  if (outfile == (FILE *) NULL) {
    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) NULL; 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;
  while (tptr != (tetrahedron *) NULL) {
    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");

  if (numelemattrib > 0) {
    // Output tetrahedra region attributes.
    fprintf(outfile, "CELL_DATA %d\n", NEL);
    fprintf(outfile, "SCALARS cell_scalars int 1\n");
    fprintf(outfile, "LOOKUP_TABLE default\n");
    tetrahedrons->traversalinit();
    tptr = tetrahedrontraverse();
    while (tptr != (tetrahedron *) NULL) {
      fprintf(outfile, "%d\n", (int) elemattribute(tptr, numelemattrib - 1));
      tptr = tetrahedrontraverse();
    }
    fprintf(outfile, "\n");
  }

  fclose(outfile);
}

void tetgenmesh::out_surfmesh_vtk(char* ofilename, int mesh_idx)
{
  FILE *outfile;
  char vtkfilename[FILENAMESIZE];
  triface abuttingtet;
  face faceloop;
  point pointloop, torg, tdest, tapex;
  double x, y, z;
  int n1, n2, n3;
  int nnodes = 3;
  int celltype = 5; // triangle

  int t1ver;

  if (b->order == 2) {
    printf("  Write VTK not implemented for order 2 elements \n");
    return;
  }

  int NEL = subfaces->items; //tetrahedrons->items - hullsize;
  int NN = points->items;

  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
    //strcpy(vtkfilename, ofilename);
    sprintf(vtkfilename, "%s.%d.vtk", ofilename, mesh_idx);
  } else if (b->outfilename[0] != '\0') {
    strcpy(vtkfilename, b->outfilename);
    strcat(vtkfilename, ".surf.vtk");
  } else {
    strcpy(vtkfilename, "noname.surf.vtk");
  }

  if (!b->quiet) {
    printf("Writing %s.\n", vtkfilename);
  }
  outfile = fopen(vtkfilename, "w");
  if (outfile == (FILE *) NULL) {
    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) NULL; 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*(3+1));
  //NEL rows, each has 1 type id + 3 node id's

  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  //facenumber = firstindex; // in->firstnumber;
  while (faceloop.sh != (shellface *) NULL) {
    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 != NULL) {
      if (ishulltet(abuttingtet)) {
        fsymself(abuttingtet);
      }
    }
    if (abuttingtet.tet != NULL) {
      torg = org(abuttingtet);
      tdest = dest(abuttingtet);
      tapex = apex(abuttingtet);
    } else {
      // This may happen when only a surface mesh be generated.
      torg = sorg(faceloop);
      tdest = sdest(faceloop);
      tapex = sapex(faceloop);
    }
    
    n1 = pointmark(torg) - in->firstnumber;
    n2 = pointmark(tdest) - in->firstnumber;
    n3 = pointmark(tapex) - in->firstnumber;

    fprintf(outfile, "%d  %4d %4d %4d\n", nnodes, n1, n2, n3);
    //facenumber++;
    faceloop.sh = shellfacetraverse(subfaces);
  }
  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");

  if (in->facetmarkerlist != NULL) { //if (numelemattrib > 0) {
    // Output tetrahedra region attributes.
    fprintf(outfile, "CELL_DATA %d\n", NEL);
    fprintf(outfile, "SCALARS cell_scalars int 1\n");
    fprintf(outfile, "LOOKUP_TABLE default\n");
    
    subfaces->traversalinit();
    faceloop.sh = shellfacetraverse(subfaces);
    //facenumber = firstindex; // in->firstnumber;
    while (faceloop.sh != (shellface *) NULL) {
      fprintf(outfile, "%d\n", (int) shellmark(faceloop));
      //facenumber++;
      faceloop.sh = shellfacetraverse(subfaces);
    }
    fprintf(outfile, "\n");
  }

  fclose(outfile);
}

//============================================================================//
//                                                                            //
// out_intersected_facets()    Save skipped subfaces.                         //
//                                                                            //
//============================================================================//

void tetgenmesh::out_intersected_facets()
{
  char FileName[1024], *sptr;

  strcpy(FileName, b->outfilename);
  sptr = strrchr(b->outfilename, '.');
  if (sptr != NULL) *sptr = '\0';
  strcat(b->outfilename, "_skipped");
  
  outnodes(NULL); 

  strcpy(b->outfilename, FileName); // Restore the original file name.

  strcpy(FileName, b->outfilename);
  sptr = strrchr(FileName, '.');
  if (sptr != NULL) *sptr = '\0';
  strcat(FileName, "_skipped.face");
  FILE *fout = fopen(FileName, "w");

  if (!b->quiet) {
    printf("Writing %s\n", FileName);
  }

  // Determine the first index (0 or 1).
  int firstindex = b->zeroindex ? 0 : in->firstnumber;
  int shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  int facenumber = firstindex; // in->firstnumber;

  fprintf(fout, "%ld 1\n", skipped_facet_list->objects);

  for (int i = 0; i < (int) skipped_facet_list->objects; i++) {
    badface *bf = (badface *) fastlookup(skipped_facet_list, i);
    fprintf(fout, "%d  %d %d %d  %d\n", facenumber,
            pointmark(bf->forg) - shift,
            pointmark(bf->fdest) - shift,
            pointmark(bf->fapex) - shift,
            (int) bf->key);
    // remove it from the pool of subfaces (do not output them to .face).
    shellfacedealloc(subfaces, bf->ss.sh);
    facenumber++;
  }

  fclose(fout);
}


//                                                                            //
//                                                                            //
//== 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[13], ts[6]; // 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 && (bgmin->numberofpoints > 0)) {
    m.bgm = new tetgenmesh(); // Create an empty background mesh.
    m.bgm->b = b;
    m.bgm->in = bgmin;
  }

  m.initializepools();
  m.transfernodes();


  tv[1] = clock();

  if (b->refine) { // -r
    m.reconstructmesh();
  } else { // -p
    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 && !b->refine) { // -p
    m.meshsurface();

    ts[0] = clock();

    if (!b->quiet) {
      printf("Surface mesh seconds:  %g\n", ((REAL)(ts[0]-tv[2])) / cps);
    }
  }


  tv[3] = clock();

  if ((b->metric) && (m.bgm != NULL)) { // -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 && !b->refine) { // -p
    if (!b->cdt) { // no -D
      m.recoverboundary(ts[0]);
    } else {
      m.constraineddelaunay(ts[0]);
    }

    ts[1] = clock();

    if (!b->quiet) {
      if (!b->cdt) { // no -D
        printf("Boundary recovery ");
      } else {
        printf("Constrained Delaunay ");
      }
      printf("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);
      }
    }

    if (m.skipped_facet_list != NULL) {
      if (!b->quiet) {
        printf("\n!!! %ld input triangles are skipped due to self-intersections.\n",
               m.skipped_facet_list->objects);
      }

      if (!b->nofacewritten) m.out_intersected_facets();
      delete m.skipped_facet_list;
      m.skipped_facet_list = NULL;

      if (!b->nonodewritten) m.outnodes(out);
      if (!b->noelewritten)  m.outelements(out);
      if (!b->nofacewritten) m.outsubfaces(out);
      if (!b->nofacewritten) m.outsubsegments(out);

      terminatetetgen(NULL, 3); // This is not a normal exit.
    }

    if (b->diagnose) { // -d
      if (!b->quiet) {
        printf("\nThe input surface mesh is correct.\n");
      }
      return;
    }

    m.carveholes();

    ts[2] = clock();

    if (!b->quiet) {
      printf("Exterior tets removal seconds:  %g\n",((REAL)(ts[2]-ts[1]))/cps);
    }

    ts[3] = clock();

    if ((!b->cdt || b->nobisect) && (b->supsteiner_level > 0)) { // no -D, -Y/1
      if (m.subvertstack->objects > 0l) {
        m.suppresssteinerpoints();
        if (!b->quiet) {
          printf("Steiner suppression seconds:  %g\n", ((REAL)(ts[3]-ts[2]))/cps);
        }
      }
    }
    
    if ((b->nobisect > 1)) { // -YY
      if ((m.st_segref_count > 0) || (m.st_facref_count > 0)) {
        if (!b->nonodewritten) m.outnodes(out);
        if (!b->noelewritten)  m.outelements(out);
        if (!b->nofacewritten) m.outsubfaces(out);
        if (!b->nofacewritten) m.outsubsegments(out);
        printf("!! Boundary contains Steiner points (-YY option). Program stopped.\n");
        terminatetetgen(&m, 200);
      }
    }
  }

  tv[5] = clock();

  if (b->metric || b->coarsen) { // -m or -R
    m.meshcoarsening();
  }

  tv[6] = clock();

  if (!b->quiet) {
    if (b->metric || b->coarsen) {
      printf("Mesh coarsening seconds:  %g\n", ((REAL)(tv[6] - tv[5])) / cps);
    }
  }

  if (b->plc || (b->refine && b->quality && (in->refine_elem_list == NULL))) {
    if (!b->quiet) {
      printf("Recovering Delaunayness...\n");
    }
    m.recoverdelaunay();
  }

  tv[7] = clock();

  if (b->plc || (b->refine && b->quality && (in->refine_elem_list == NULL))) {
    if (!b->quiet) {
      printf("Delaunay recovery seconds:  %g\n", ((REAL)(tv[7] - tv[6]))/cps);
    }
  }

  if ((b->plc || b->refine) && b->insertaddpoints) { // -i
    if ((addin != NULL) && (addin->numberofpoints > 0)) {
      m.insertconstrainedpoints(addin); 
    }
  }

  tv[8] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && b->insertaddpoints) { // -i
      if ((addin != NULL) && (addin->numberofpoints > 0)) {
        printf("Constrained points seconds:  %g\n", ((REAL)(tv[8]-tv[7]))/cps);
      }
    }
  }
  if (b->quality) { // -q
    m.delaunayrefinement();    
  }

  tv[9] = clock();

  if (!b->quiet) {
    if (b->quality) {
      printf("Refinement seconds:  %g\n", ((REAL)(tv[9] - tv[8])) / cps);
    }
  }

  if ((b->plc || b->quality) &&
      (b->smooth_maxiter > 0) &&
      ((m.st_volref_count > 0) || (m.st_facref_count > 0))) {
    m.smooth_vertices(); // m.optimizemesh(ts[0]);
  }

  tv[10] = clock();

  if (!b->quiet) {
    if ((b->plc || b->quality) &&
        (b->smooth_maxiter > 0) &&
        ((m.st_volref_count > 0) || (m.st_facref_count > 0))) {
      printf("Mesh smoothing seconds:  %g\n", ((REAL)(tv[10] - tv[9])) / cps);
    }
  }

  if (b->plc || b->quality) {
    m.improve_mesh();
  }

  tv[11] = clock();

  if (!b->quiet) {
    if (b->plc || b->quality) {
      printf("Mesh improvement seconds:  %g\n", ((REAL)(tv[11] - tv[10])) / 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 *) NULL) {
    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");
    }
    m.indexelements();
  } 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(NULL, 0); // b->outfilename
  }

  if (!out && b->vtksurfview) {
    m.out_surfmesh_vtk(NULL, 0);
  }

  if (b->neighout) {
    m.outneighbors(out);
  }

  if (b->voroout) {
    m.outvoronoi(out);
  }


  tv[12] = clock();

  if (!b->quiet) {
    printf("\nOutput seconds:  %g\n", ((REAL)(tv[12] - tv[11])) / cps);
    printf("Total running seconds:  %g\n", ((REAL)(tv[12] - tv[0])) / cps);
  }

  if (b->docheck) {
    m.check_mesh(0);
    if (b->plc || b->refine) {
      m.check_shells();
      m.check_segments();
    }
    if (b->docheck > 1) {
      m.check_delaunay();
    }
  }

  if (!b->quiet) {
    m.statistics();
  }
}

#ifndef TETLIBRARY

//============================================================================//
//                                                                            //
// main()    The command line interface of TetGen.                            //
//                                                                            //
//============================================================================//

//int tetgen_main(int argc, char *argv[])
int main(int argc, char *argv[])

#else // with TETLIBRARY

//============================================================================//
//                                                                            //
// tetrahedralize()    The library interface of TetGen.                       //
//                                                                            //
//============================================================================//

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(NULL, 10);
  }

  // Read input files.
  if (b.refine) { // -r
    if (!in.load_tetmesh(b.infilename, (int) b.object)) {
      terminatetetgen(NULL, 10);
    }
  } else { // -p
    if (!in.load_plc(b.infilename, (int) b.object)) {
      terminatetetgen(NULL, 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, NULL, &addin, &bgmin);

  return 0;

#else // with TETLIBRARY

  if (!b.parse_commandline(switches)) {
    terminatetetgen(NULL, 10);
  }
  tetrahedralize(&b, in, out, addin, bgmin);

#endif // not TETLIBRARY
}

//                                                                            //
//                                                                            //
//== main_cxx ================================================================//

