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

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetgen.cxx                                                                //
//                                                                           //
// The TetGen library and program.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "tetgen.h"

#if defined(_MSVC)
#pragma warning(disable: 4266)
#pragma warning(disable: 4267)
#pragma warning(disable: 4996)
#pragma warning(disable: 4311)
#pragma warning(disable: 4101)
#pragma warning(disable: 4800)
#endif

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// terminatetetgen()    Terminate TetGen with a given exit code.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void terminatetetgen(int x)
{
#ifdef TETLIBRARY
  throw x;
#else
  exit(x);
#endif // #ifdef TETLIBRARY
}

//
// Begin of class 'tetgenio' implementation
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initialize()    Initialize all variables of 'tetgenio'.                   //
//                                                                           //
// It is called by the only class constructor 'tetgenio()' implicitly. Thus, //
// all variables are guaranteed to be initialized. Each array is initialized //
// to be a 'NULL' pointer, and its length is equal zero. Some variables have //
// their default value, 'firstnumber' equals zero, 'mesh_dim' equals 3,  and //
// 'numberofcorners' equals 4.  Another possible use of this routine is to   //
// call it before to re-use an object.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::initialize()
{
  firstnumber = 0;              // Default item index is numbered from Zero.
  mesh_dim = 3;                              // Default mesh dimension is 3.
  useindex = true;

  pointlist = (REAL *) NULL;
  pointattributelist = (REAL *) NULL;
  pointmtrlist = (REAL *) NULL;
  pointmarkerlist = (int *) NULL;
  numberofpoints = 0;
  numberofpointattributes = 0;
  numberofpointmtrs = 0;

  tetrahedronlist = (int *) NULL;
  tetrahedronattributelist = (REAL *) NULL;
  tetrahedronvolumelist = (REAL *) NULL;
  neighborlist = (int *) NULL;
  numberoftetrahedra = 0;
  numberofcorners = 4;                   // Default is 4 nodes per element.
  numberoftetrahedronattributes = 0;

  trifacelist = (int *) NULL;
  adjtetlist = (int *) NULL;
  trifacemarkerlist = (int *) NULL;
  numberoftrifaces = 0; 

  facetlist = (facet *) NULL;
  facetmarkerlist = (int *) NULL;
  numberoffacets = 0; 

  edgelist = (int *) NULL;
  edgemarkerlist = (int *) NULL;
  numberofedges = 0;

  holelist = (REAL *) NULL;
  numberofholes = 0;

  regionlist = (REAL *) NULL;
  numberofregions = 0;

  facetconstraintlist = (REAL *) NULL;
  numberoffacetconstraints = 0;
  segmentconstraintlist = (REAL *) NULL;
  numberofsegmentconstraints = 0;

  pbcgrouplist = (pbcgroup *) NULL;
  numberofpbcgroups = 0;

  vpointlist = (REAL *) NULL;
  vedgelist = (voroedge *) NULL;
  vfacetlist = (vorofacet *) NULL; 
  vcelllist = (int **) NULL; 
  numberofvpoints = 0;
  numberofvedges = 0;
  numberofvfacets = 0;
  numberofvcells = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// deinitialize()    Free the memory allocated in 'tetgenio'.                //
//                                                                           //
// It is called by the class destructor '~tetgenio()' implicitly. Hence, the //
// occupied memory by arrays of an object will be automatically released on  //
// the deletion of the object. However, this routine assumes that the memory //
// is allocated by C++ memory allocation operator 'new', thus it is freed by //
// the C++ array deletor 'delete []'. If one uses the C/C++ library function //
// 'malloc()' to allocate memory for arrays, one has to free them with the   //
// 'free()' function, and call routine 'initialize()' once to disable this   //
// routine on deletion of the object.                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::deinitialize()
{
  facet *f;
  polygon *p;
  pbcgroup *pg;
  int i, j;

  if (pointlist != (REAL *) NULL) {
    delete [] pointlist;
  }
  if (pointattributelist != (REAL *) NULL) {
    delete [] pointattributelist;
  }
  if (pointmtrlist != (REAL *) NULL) {
    delete [] pointmtrlist;
  }
  if (pointmarkerlist != (int *) NULL) {
    delete [] pointmarkerlist;
  }

  if (tetrahedronlist != (int *) NULL) {
    delete [] tetrahedronlist;
  }
  if (tetrahedronattributelist != (REAL *) NULL) {
    delete [] tetrahedronattributelist;
  }
  if (tetrahedronvolumelist != (REAL *) NULL) {
    delete [] tetrahedronvolumelist;
  }
  if (neighborlist != (int *) NULL) {
    delete [] neighborlist;
  }

  if (trifacelist != (int *) NULL) {
    delete [] trifacelist;
  }
  if (adjtetlist != (int *) NULL) {
    delete [] adjtetlist;
  }
  if (trifacemarkerlist != (int *) NULL) {
    delete [] trifacemarkerlist;
  }

  if (edgelist != (int *) NULL) {
    delete [] edgelist;
  }
  if (edgemarkerlist != (int *) NULL) {
    delete [] edgemarkerlist;
  }

  if (facetlist != (facet *) NULL) {
    for (i = 0; i < numberoffacets; i++) {
      f = &facetlist[i];
      for (j = 0; j < f->numberofpolygons; j++) {
        p = &f->polygonlist[j];
        delete [] p->vertexlist;
      }
      delete [] f->polygonlist;
      if (f->holelist != (REAL *) NULL) {
        delete [] f->holelist;
      }
    }
    delete [] facetlist;
  }
  if (facetmarkerlist != (int *) NULL) {
    delete [] facetmarkerlist;
  }

  if (holelist != (REAL *) NULL) {
    delete [] holelist;
  }
  if (regionlist != (REAL *) NULL) {
    delete [] regionlist;
  }
  if (facetconstraintlist != (REAL *) NULL) {
    delete [] facetconstraintlist;
  }
  if (segmentconstraintlist != (REAL *) NULL) {
    delete [] segmentconstraintlist;
  }
  if (pbcgrouplist != (pbcgroup *) NULL) {
    for (i = 0; i < numberofpbcgroups; i++) {
      pg = &(pbcgrouplist[i]);
      if (pg->pointpairlist != (int *) NULL) {
        delete [] pg->pointpairlist;
      }
    }
    delete [] pbcgrouplist;
  }
  if (vpointlist != (REAL *) NULL) {
    delete [] vpointlist;
  }
  if (vedgelist != (voroedge *) NULL) {
    delete [] vedgelist;
  }
  if (vfacetlist != (vorofacet *) NULL) {
    for (i = 0; i < numberofvfacets; i++) {
      delete [] vfacetlist[i].elist;
    }
    delete [] vfacetlist;
  }
  if (vcelllist != (int **) NULL) {
    for (i = 0; i < numberofvcells; i++) {
      delete [] vcelllist[i];
    }
    delete [] vcelllist;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node_call()    Load a list of nodes.                                 //
//                                                                           //
// It is a support routine for routines: 'load_nodes()', 'load_poly()', and  //
// 'load_tetmesh()'.  '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. 'infilename' is the  //
// name of the file being read,  it is only appeared in error message.       //
//                                                                           //
// 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, 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) {
    printf("Error:  Out of memory.\n");
    terminatetetgen(1);
  }
  if (numberofpointattributes > 0) {
    pointattributelist = new REAL[numberofpoints * numberofpointattributes];
    if (pointattributelist == (REAL *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }
  if (markers) {
    pointmarkerlist = new int[numberofpoints];
    if (pointmarkerlist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }

  // Read the point section.
  index = 0;
  attribindex = 0;
  for (i = 0; i < numberofpoints; i++) {
    stringptr = readnumberline(inputline, infile, infilename);
    if (useindex) {
      if (i == 0) {
        firstnode = (int) strtol (stringptr, &stringptr, 0);
        if ((firstnode == 0) || (firstnode == 1)) {
          firstnumber = firstnode;
        }
      }
      stringptr = findnextnumber(stringptr);
    } // if (useindex)
    if (*stringptr == '\0') {
      printf("Error:  Point %d has no x coordinate.\n", firstnumber + i);
      break;
    }
    x = (REAL) strtod(stringptr, &stringptr);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
      break;
    }
    y = (REAL) strtod(stringptr, &stringptr);
    if (mesh_dim == 3) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        printf("Error:  Point %d has no z coordinate.\n", firstnumber + i);
        break;
      }
      z = (REAL) strtod(stringptr, &stringptr);
    } else {
      z = 0.0; // mesh_dim == 2;
    }
    pointlist[index++] = x;
    pointlist[index++] = y;
    pointlist[index++] = z;
    // Read the point attributes.
    for (j = 0; j < numberofpointattributes; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        attrib = 0.0;
      } else {
        attrib = (REAL) strtod(stringptr, &stringptr);
      }
      pointattributelist[attribindex++] = attrib;
    }
    if (markers) {
      // Read a point marker.
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        currentmarker = 0;
      } else {
        currentmarker = (int) strtol (stringptr, &stringptr, 0);
      }
      pointmarkerlist[i] = currentmarker;
    }
  }
  if (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;
    }
    numberofpoints = 0;
    return false;
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node()    Load a list of nodes from a .node file.                    //
//                                                                           //
// 'filename' is the inputfile without suffix. The node list is in 'filename.//
// node'. On completion, the node list is returned in 'pointlist'.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_node(char* filename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  int markers;

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

  // Try to open a .node file.
  infile = fopen(innodefilename, "r");
  if (infile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
    return false;
  }
  printf("Opening %s.\n", innodefilename);  
  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, innodefilename);
  // Is this list of points generated from rbox?
  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 = 3;
    } else {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      numberofpointattributes = 0;
    } else {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;
    } else {
      markers = (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;
  }

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

  // Load the list of nodes.
  if (!load_node_call(infile, markers, innodefilename)) {
    fclose(infile);
    return false;
  }
  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_pbc()    Load a list of pbc groups into 'pbcgrouplist'.              //
//                                                                           //
// 'filename' is the filename of the original inputfile without suffix. The  //
// pbc groups are found in file 'filename.pbc'.                              //
//                                                                           //
// This routine will be called both in load_poly() and load_tetmesh().       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_pbc(char* filename)
{
  FILE *infile;
  char pbcfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  pbcgroup *pg;
  int index, p1, p2;
  int i, j, k;

  // Pbc groups are saved in file "filename.pbc".
  strcpy(pbcfilename, filename);
  strcat(pbcfilename, ".pbc");
  infile = fopen(pbcfilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", pbcfilename);
  } else {
    // No such file. Return.
    return false;
  }

  // Read the number of pbc groups.
  stringptr = readnumberline(inputline, infile, pbcfilename);
  numberofpbcgroups = (int) strtol (stringptr, &stringptr, 0);
  if (numberofpbcgroups == 0) {
    // It looks this file contains no point.
    fclose(infile);
    return false; 
  }
  // Initialize 'pbcgrouplist';
  pbcgrouplist = new pbcgroup[numberofpbcgroups];

  // Read the list of pbc groups.
  for (i = 0; i < numberofpbcgroups; i++) {
    pg = &(pbcgrouplist[i]);
    // Initialize pbcgroup i;
    pg->numberofpointpairs = 0;
    pg->pointpairlist = (int *) NULL;
    // Read 'fmark1', 'fmark2'.
    stringptr = readnumberline(inputline, infile, pbcfilename);
    if (*stringptr == '\0') break;
    pg->fmark1 = (int) strtol(stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') break;
    pg->fmark2 = (int) strtol(stringptr, &stringptr, 0);
    // Read 'transmat'.
    do {
      stringptr = readline(inputline, infile, NULL);
    } while ((*stringptr != '[') && (*stringptr != '\0'));
    if (*stringptr == '\0') break;
    for (j = 0; j < 4; j++) {
      for (k = 0; k < 4; k++) {
        // Read the entry of [j, k].
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          // Try to read another line.
          stringptr = readnumberline(inputline, infile, pbcfilename);
          if (*stringptr == '\0') break;
        }
        pg->transmat[j][k] = (REAL) strtod(stringptr, &stringptr);
      }
      if (k < 4) break; // Not complete!
    }
    if (j < 4) break; // Not complete!
    // Read 'numberofpointpairs'.
    stringptr = readnumberline(inputline, infile, pbcfilename);
    if (*stringptr == '\0') break;
    pg->numberofpointpairs = (int) strtol(stringptr, &stringptr, 0);
    if (pg->numberofpointpairs > 0) {
      pg->pointpairlist = new int[pg->numberofpointpairs * 2];
      // Read the point pairs.
      index = 0;
      for (j = 0; j < pg->numberofpointpairs; j++) {
        stringptr = readnumberline(inputline, infile, pbcfilename);
        p1 = (int) strtol(stringptr, &stringptr, 0);
        stringptr = findnextnumber(stringptr);
        p2 = (int) strtol(stringptr, &stringptr, 0);
        pg->pointpairlist[index++] = p1;
        pg->pointpairlist[index++] = p2;
      }
    }
  }
  fclose(infile);

  if (i < numberofpbcgroups) {
    // Failed to read to additional points due to some error.
    delete [] pbcgrouplist;
    pbcgrouplist = (pbcgroup *) NULL;
    numberofpbcgroups = 0;
    return false;
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_var()    Load variant constraints applied on facets, segments, nodes.//
//                                                                           //
// 'filename' is the filename of the original inputfile without suffix. The  //
// constraints are found in file 'filename.var'.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

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

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

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

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_mtr()    Load a size specification map from file.                    //
//                                                                           //
// 'filename' is the filename of the original inputfile without suffix. The  //
// size map is found in file 'filename.mtr'.                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  strcpy(mtrfilename, filename);
  strcat(mtrfilename, ".mtr");
  infile = fopen(mtrfilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", mtrfilename);
  } else {
    // No such file. Return.
    return false;
  }

  // Read number of points, number of columns (1, 3, or 6).
  stringptr = readnumberline(inputline, infile, mtrfilename);
  stringptr = findnextnumber(stringptr); // Skip number of points.
  if (*stringptr != '\0') {
    numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0);
  }
  if (numberofpointmtrs == 0) {
    // Column number doesn't match. Set a default number (1).
    numberofpointmtrs = 1;
  }

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

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_poly()    Load a piecewise linear complex from a .poly or .smesh.    //
//                                                                           //
// 'filename' is the inputfile without suffix. The PLC is in 'filename.poly' //
// or 'filename.smesh', and possibly plus 'filename.node' (when the first    //
// line of the file starts with a zero).                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

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

  // First assume it is a .poly file.
  smesh = 0;
  // Try to open a .poly file.
  polyfile = fopen(inpolyfilename, "r");
  if (polyfile == (FILE *) NULL) {
    // .poly doesn't exist! Try to open a .smesh file.
    polyfile = fopen(insmeshfilename, "r");
    if (polyfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot access file %s and %s.\n",
             inpolyfilename, insmeshfilename);
      return false;
    } else {
      printf("Opening %s.\n", insmeshfilename);
    }
    smesh = 1;
  } else {
    printf("Opening %s.\n", inpolyfilename);
  }
  // Initialize the default values.
  mesh_dim = 3;  // Three-dimemsional accoordinates.
  numberofpointattributes = 0;  // no point attribute.
  markers = 0;  // no boundary marker.
  // Read number of points, number of dimensions, number of point
  //   attributes, and number of boundary markers.
  stringptr = readnumberline(inputline, polyfile, inpolyfilename);
  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 (numberofpoints > 0) {
    readnodefile = 0;
    if (smesh) {
      infilename = insmeshfilename;
    } else {
      infilename = inpolyfilename;
    } 
    infile = polyfile;
  } else {
    // If the .poly or .smesh file claims there are zero points, that
    //   means the points should be read from a separate .node file.
    readnodefile = 1;
    infilename = innodefilename;
  }

  if (readnodefile) {
    // Read the points from the .node file.
    printf("Opening %s.\n", innodefilename);
    infile = fopen(innodefilename, "r");
    if (infile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
      return false;
    }
    // Initialize the default values.
    mesh_dim = 3;  // Three-dimemsional accoordinates.
    numberofpointattributes = 0;  // no point attribute.
    markers = 0;  // no boundary marker.
    // Read number of points, number of dimensions, number of point
    //   attributes, and number of boundary markers.
    stringptr = readnumberline(inputline, infile, innodefilename);
    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 ((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;
  }

  // Load the list of nodes.
  if (!load_node_call(infile, markers, infilename)) {
    fclose(infile);
    return false;
  }

  if (readnodefile) {
    fclose(infile);
  }

  facet *f;
  polygon *p;

  if (mesh_dim == 3) {

    // Read number of facets and number of boundary markers.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    numberoffacets = (int) strtol (stringptr, &stringptr, 0);
    if (numberoffacets <= 0) {
      // No facet list, return.
      fclose(polyfile);
      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, polyfile, inpolyfilename);
        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, polyfile, inpolyfilename);
          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, polyfile, inpolyfilename);
              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, polyfile, inpolyfilename);
            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(polyfile);
        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, polyfile, 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, polyfile, inpolyfilename);
            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(polyfile);
        return false;
      }
    }

    // Read the hole section.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    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, polyfile, inpolyfilename);
        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(polyfile);
        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, polyfile, 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, polyfile, inpolyfilename);
        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(polyfile);
        return false;
      }
    }

  } else {

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

  }

  // End of reading poly/smesh file.
  fclose(polyfile);

  // Try to load a .var file if it exists.
  load_var(filename);
  // Try to load a .mtr file if it exists.
  load_mtr(filename);
  // Try to read a .pbc file if it exists.
  load_pbc(filename);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_off()    Load a polyhedron described in 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.                               //
//                                                                           //
// 'filename' is a input filename with extension .off or without extension ( //
// the .off will be added in this case). On completion, the polyhedron is    //
// returned in 'pointlist' and 'facetlist'.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_off(char* filename)
{
  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;

  strncpy(infilename, filename, 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("File I/O Error:  Unable to open file %s\n", infilename);
    return false;
  }
  printf("Opening %s.\n", infilename);

  // OFF requires the index starts from '0'.
  firstnumber = 0;

  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];
        }
        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);
      }
      ifaces++;
    } else {
      // Should never get here
      printf("Found extra text starting at line %d in file %s\n", line_count,
             infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Check whether read all points
  if (iverts != nverts) {
    printf("Expected %d vertices, but read only %d vertices in file %s\n",
           nverts, iverts, infilename);
    return false;
  }

  // Check whether read all faces
  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 described in a .ply file.                 //
//                                                                           //
// 'filename' is the file name with extension .ply or without extension (the //
// .ply will be added in this case).                                         //
//                                                                           //
// 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.                                                          //
//                                                                           //
// On completion, 'pointlist' and 'facetlist' together return the polyhedron.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_ply(char* filename)
{
  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;

  strncpy(infilename, filename, 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);

  // PLY requires the index starts from '0'.
  firstnumber = 0;

  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];
              }
            }
          }
          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);
      }
      ifaces++;
    } else {
      // Should never get here
      printf("Found extra text starting at line %d in file %s\n", line_count,
             infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Check whether read all points
  if (iverts != nverts) {
    printf("Expected %d vertices, but read only %d vertices in file %s\n",
           nverts, iverts, infilename);
    return false;
  }

  // Check whether read all faces
  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 described in a .stl file.               //
//                                                                           //
// 'filename' is the file name with extension .stl or without extension (the //
// .stl will be added in this case).                                         //
//                                                                           //
// 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.                                                     //
//                                                                           //
// On completion, 'pointlist' and 'facetlist' together return the polyhedron.//
// Note: After load_stl(), there exist many duplicated points in 'pointlist'.//
// They will be unified during the Delaunay tetrahedralization process.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_stl(char* filename)
{
  FILE *fp;
  tetgenmesh::list *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, filename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) {
    strcat(infilename, ".stl");
  }

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

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

  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) {
          coord = (double *) plist->append(NULL);
          for (i = 0; i < 3; i++) {
            bufferp = findnextnumber(bufferp);
            if (*bufferp == '\0') {
              printf("Syntax error reading vertex coords on line %d\n",
                   line_count);
              delete plist;
              fclose(fp);
              return false;
            }
            coord[i] = (REAL) strtod(bufferp, &bufferp);
          }
        }
      }
    }
  }
  fclose(fp);

  nverts = plist->len();
  // 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 *) (* 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 described in .mesh file.              //
//                                                                           //
// 'filename' is the file name with extension .mesh or without entension (   //
// the .mesh will be added in this case). .mesh is the file format of Medit, //
// a user-friendly interactive mesh viewing program.                         //
//                                                                           //
// This routine ONLY reads the sections containing vertices, triangles, and  //
// quadrilaters. Other sections (such as tetrahedra, edges, ...) are ignored.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_medit(char* filename)
{
  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 line_count = 0;
  int corners = 0; // 3 (triangle) or 4 (quad).
  int i, j;

  strncpy(infilename, filename, 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);

  // Default uses the index starts from '1'.
  firstnumber = 1;

  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);
        // 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 {
              assert((j == 2) && (dimension == 2));
              coord[j] = 0.0;
            }
            bufferp = findnextnumber(bufferp);
          }
        }
        continue;
      }
    } 
    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 (numberoffacets > 0) {
            // facetlist has already been allocated. Enlarge arrays.
            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];
          }
        }
        // Read the following list of faces.
        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);
            if (firstnumber == 1) {
              // Check if a '0' index appears.
              if (p->vertexlist[j] == 0) {
                // The first index is set to be 0.
                firstnumber = 0;
              }
            }
            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;
      }
    }
    // if (nverts > 0 && nfaces > 0) break; // Ignore other data.
  }

  // Close file
  fclose(fp);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_plc()    Load a piecewise linear complex from file.                  //
//                                                                           //
// This is main entrance for loading plcs from different file formats into   //
// tetgenio.  'filename' is the input file name without extention. 'object'  //
// indicates which file format is used to describ the plc.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_plc(char* filename, int object)
{
  enum tetgenbehavior::objecttype type;

  type = (enum tetgenbehavior::objecttype) object;
  switch (type) {
  case tetgenbehavior::NODES:
    return load_node(filename);
  case tetgenbehavior::POLY:
    return load_poly(filename);
  case tetgenbehavior::OFF:
    return load_off(filename);
  case tetgenbehavior::PLY:
    return load_ply(filename);
  case tetgenbehavior::STL:
    return load_stl(filename);
  case tetgenbehavior::MEDIT:
    return load_medit(filename);
  default:
    return load_poly(filename);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_tetmesh()    Load a tetrahedral mesh from files.                     //
//                                                                           //
// 'filename' is the inputfile without suffix.  The nodes of the tetrahedral //
// mesh is in "filename.node",  the elements is in "filename.ele", if the    //
// "filename.face" and "filename.vol" exists, they will also be read.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_tetmesh(char* filename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inelefilename[FILENAMESIZE];
  char infacefilename[FILENAMESIZE];
  char inedgefilename[FILENAMESIZE];
  char involfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr, *infilename;
  REAL attrib, volume;
  int volelements;
  int markers, corner;
  int index, attribindex;
  int i, j;

  // Assembling the actual file names we want to open.
  strcpy(innodefilename, filename);
  strcpy(inelefilename, filename);
  strcpy(infacefilename, filename);
  strcpy(inedgefilename, filename);
  strcpy(involfilename, filename);
  strcat(innodefilename, ".node");
  strcat(inelefilename, ".ele");
  strcat(infacefilename, ".face");
  strcat(inedgefilename, ".edge");
  strcat(involfilename, ".vol");

  // Read the points from a .node file.
  infilename = innodefilename;
  printf("Opening %s.\n", infilename);
  infile = fopen(infilename, "r");
  if (infile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot access file %s.\n", infilename);
    return false;
  }
  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, infilename); 
  // Is this list of points generated from rbox?
  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 = 3;
    } else {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      numberofpointattributes = 0;
    } else {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;  // Default value.
    } else {
      markers = (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, infilename);
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    // There is no index column.
    useindex = 0;
  }

  // Load the list of nodes.
  if (!load_node_call(infile, markers, infilename)) {
    fclose(infile);
    return false;
  }
  fclose(infile);

  // Read the elements from an .ele file.
  if (mesh_dim == 3) {
    infilename = inelefilename;
    infile = fopen(infilename, "r");
    if (infile != (FILE *) NULL) {
      printf("Opening %s.\n", infilename);
      // 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);
      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.
      if (numberoftetrahedra > 0) {
        tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; 
        if (tetrahedronlist == (int *) NULL) {
          printf("Error:  Out of memory.\n");
          terminatetetgen(1);
        }
        // Allocate memory for output tetrahedron attributes if necessary.
        if (numberoftetrahedronattributes > 0) {
          tetrahedronattributelist = new REAL[numberoftetrahedra *
                                          numberoftetrahedronattributes];
          if (tetrahedronattributelist == (REAL *) NULL) {
            printf("Error:  Out of memory.\n");
            terminatetetgen(1);
          }
        }
      }
      // Read the list of tetrahedra.
      index = 0;
      attribindex = 0;
      for (i = 0; i < numberoftetrahedra; i++) {
        // Read tetrahedron index and the tetrahedron's corners.
        stringptr = readnumberline(inputline, infile, infilename);
        for (j = 0; j < numberofcorners; j++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            printf("Error:  Tetrahedron %d is missing vertex %d in %s.\n",
                   i + firstnumber, j + 1, infilename);
            terminatetetgen(1);
          }
          corner = (int) strtol(stringptr, &stringptr, 0);
          if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
            printf("Error:  Tetrahedron %d has an invalid vertex index.\n",
                   i + firstnumber);
            terminatetetgen(1);
          }
          tetrahedronlist[index++] = corner;
        }
        // Read the tetrahedron's attributes.
        for (j = 0; j < numberoftetrahedronattributes; j++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            attrib = 0.0;
          } else {
            attrib = (REAL) strtod(stringptr, &stringptr);
          }
          tetrahedronattributelist[attribindex++] = attrib;
        }
      }
      fclose(infile);
    }
  } // if (meshdim == 3)
  
  // Read the hullfaces or subfaces from a .face file if it exists.
  if (mesh_dim == 3) {
    infilename = infacefilename;
  } else {
    infilename = inelefilename;
  }
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", infilename);
    // 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) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
      if (markers) {
        trifacemarkerlist = new int[numberoftrifaces * 3];
        if (trifacemarkerlist == (int *) NULL) {
          printf("Error:  Out of memory.\n");
          terminatetetgen(1);
        }
      }
    }
    // Read the list of faces.
    index = 0;
    for (i = 0; i < numberoftrifaces; i++) {
      // Read face index and the face's three corners.
      stringptr = readnumberline(inputline, infile, infilename);
      for (j = 0; j < 3; j++) {
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          printf("Error:  Face %d is missing vertex %d in %s.\n",
                 i + firstnumber, j + 1, infilename);
          terminatetetgen(1);
        }
        corner = (int) strtol(stringptr, &stringptr, 0);
        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
          printf("Error:  Face %d has an invalid vertex index.\n",
                 i + firstnumber);
          terminatetetgen(1);
        }
        trifacelist[index++] = corner;
      }
      // 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);
  }

  // Read the boundary edges from a .edge file if it exists.
  infilename = inedgefilename;
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", infilename);
    // Read number of boundary edges.
    stringptr = readnumberline(inputline, infile, infilename);
    numberofedges = (int) strtol (stringptr, &stringptr, 0);
    if (numberofedges > 0) {
      edgelist = new int[numberofedges * 2];
      if (edgelist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    // Read the list of faces.
    index = 0;
    for (i = 0; i < numberofedges; i++) {
      // Read face index and the edge's two endpoints.
      stringptr = readnumberline(inputline, infile, infilename);
      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, infilename);
          terminatetetgen(1);
        }
        corner = (int) strtol(stringptr, &stringptr, 0);
        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
          printf("Error:  Edge %d has an invalid vertex index.\n",
                 i + firstnumber);
          terminatetetgen(1);
        }
        edgelist[index++] = corner;
      }
    }
    fclose(infile);
  }

  // Read the volume constraints from a .vol file if it exists.
  infilename = involfilename;
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", infilename);
    // Read number of tetrahedra.
    stringptr = readnumberline(inputline, infile, infilename);
    volelements = (int) strtol (stringptr, &stringptr, 0);
    if (volelements != numberoftetrahedra) {
      printf("Warning:  %s and %s disagree on number of tetrahedra.\n",
             inelefilename, involfilename);
      volelements = 0;
    }
    if (volelements > 0) {
      tetrahedronvolumelist = new REAL[volelements];
      if (tetrahedronvolumelist == (REAL *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    // Read the list of volume constraints.
    for (i = 0; i < volelements; i++) {
      stringptr = readnumberline(inputline, infile, infilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        volume = -1.0; // No constraint on this tetrahedron.
      } else {
        volume = (REAL) strtod(stringptr, &stringptr);
      }
      tetrahedronvolumelist[i] = volume;
    }
    fclose(infile);
  }

  // Try to load a .mtr file if it exists.
  load_mtr(filename);
  // Try to read a .pbc file if it exists.
  load_pbc(filename);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_voronoi()    Load a Voronoi diagram from files.                      //
//                                                                           //
// 'filename' is the inputfile without suffix.  The Voronoi diagram is read  //
// from files: filename.v.node, filename.v.edge, and filename.v.face.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_voronoi(char* filename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inedgefilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr, *infilename;
  voroedge *vedge;
  REAL x, y, z;
  int firstnode, corner;
  int index;
  int i, j;

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

  // Read the points from a .v.node file.
  infilename = innodefilename;
  printf("Opening %s.\n", infilename);
  infile = fopen(infilename, "r");
  if (infile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot access file %s.\n", infilename);
    return false;
  }
  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, infilename); 
  // Is this list of points generated from rbox?
  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;
    numberofvpoints = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      mesh_dim = 3;  // Default.
    } else {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    useindex = 1;  // There is an index column.
  } 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, infilename);
    numberofvpoints = (int) strtol (stringptr, &stringptr, 0);
    useindex = 0;  // No index column.
  }
  // Initialize 'vpointlist'.
  vpointlist = new REAL[numberofvpoints * 3];
  if (vpointlist == (REAL *) NULL) {
    printf("Error:  Out of memory.\n");
    terminatetetgen(1);
  }
  // Read the point section.
  index = 0;
  for (i = 0; i < numberofvpoints; 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);
      terminatetetgen(1);
    }
    x = (REAL) strtod(stringptr, &stringptr);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
      terminatetetgen(1);
    }
    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);
        terminatetetgen(1);
      }
      z = (REAL) strtod(stringptr, &stringptr);
    } else {
      z = 0.0; // mesh_dim == 2;
    }
    vpointlist[index++] = x;
    vpointlist[index++] = y;
    vpointlist[index++] = z;
  }
  fclose(infile);

  // Read the Voronoi edges from a .v.edge file if it exists.
  infilename = inedgefilename;
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    printf("Opening %s.\n", infilename);
    // Read number of boundary edges.
    stringptr = readnumberline(inputline, infile, infilename);
    numberofvedges = (int) strtol (stringptr, &stringptr, 0);
    if (numberofvedges > 0) {
      vedgelist = new voroedge[numberofvedges];
    }
    // Read the list of faces.
    index = 0;
    for (i = 0; i < numberofvedges; i++) {
      // Read edge index and the edge's two endpoints.
      stringptr = readnumberline(inputline, infile, infilename);
      vedge = &(vedgelist[i]);
      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, infilename);
          terminatetetgen(1);
        }
        corner = (int) strtol(stringptr, &stringptr, 0);
        j == 0 ? vedge->v1 = corner : vedge->v2 = corner;
      }
      if (vedge->v2 < 0) {
        for (j = 0; j < mesh_dim; j++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            printf("Error:  Edge %d is missing normal in %s.\n",
                   i + firstnumber, infilename);
            terminatetetgen(1);
          }
          vedge->vnormal[j] = (REAL) strtod(stringptr, &stringptr);
        }
        if (mesh_dim == 2) {
          vedge->vnormal[2] = 0.0;
        }
      } else {
        vedge->vnormal[0] = 0.0;
        vedge->vnormal[1] = 0.0;
        vedge->vnormal[2] = 0.0;
      }
    }
    fclose(infile);
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_nodes()    Save points to a .node file.                              //
//                                                                           //
// 'filename' is a string containing the file name without suffix.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outnodefilename, "%s.node", filename);
  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 * 2],
              pointlist[i * 2 + 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", filename);
    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.                          //
//                                                                           //
// 'filename' is a string containing the file name without suffix.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outelefilename, "%s.ele", filename);
  printf("Saving elements to %s\n", outelefilename);
  fout = fopen(outelefilename, "w");
  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");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_faces()    Save faces to a .face file.                               //
//                                                                           //
// 'filename' is a string containing the file name without suffix.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outfacefilename, "%s.face", filename);
  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.                               //
//                                                                           //
// 'filename' is a string containing the file name without suffix.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outedgefilename, "%s.edge", filename);
  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.                          //
//                                                                           //
// 'filename' is a string containing the file name without suffix.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outneighborfilename, "%s.neigh", filename);
  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.                   //
//                                                                           //
// 'filename' is a string containing the file name without suffix.  It only  //
// save the facets, holes and regions.  The nodes are saved in a .node file  //
// by routine save_nodes().                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  sprintf(outpolyfilename, "%s.poly", filename);
  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);  
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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');
  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) {
      if (infilename != (char *) NULL) {
        printf("  Error:  Unexpected end of file in %s.\n", infilename);
        terminatetetgen(1);
      }
      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;
}

//
// End of class 'tetgenio' implementation
//

static REAL PI = 3.14159265358979323846264338327950288419716939937510582;

//
// Begin of class 'tetgenbehavior' implementation
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetgenbehavior()    Initialize veriables of 'tetgenbehavior'.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenbehavior::tetgenbehavior()
{
  // Initialize command line switches.
  plc = 0;
  quality = 0;
  refine = 0;
  coarse = 0;
  metric = 0;
  minratio = 2.0;
  goodratio = 0.0;
  minangle = 20.0;
  goodangle = 0.0;
  maxdihedral = 165.0;
  mindihedral = 5.0;
  varvolume = 0;
  fixedvolume = 0;
  maxvolume = -1.0;
  regionattrib = 0;
  insertaddpoints = 0;
  diagnose = 0;
  offcenter = 0;
  conformdel = 0;
  alpha1 = sqrt(2.0);
  alpha2 = 1.0;
  alpha3 = 0.6;
  zeroindex = 0;
  facesout = 0;
  edgesout = 0;
  neighout = 0;
  voroout = 0;
  meditview = 0;
  gidview = 0;
  geomview = 0;
  optlevel = 3;
  optpasses = 3;
  order = 1;
  nojettison = 0;
  nobound = 0;
  nonodewritten = 0;
  noelewritten = 0;
  nofacewritten = 0;
  noiterationnum = 0;
  nobisect = 0;
  noflip = 0;
  steiner = -1;
  fliprepair = 1;
  nomerge = 0;
  docheck = 0;
  quiet = 0;
  verbose = 0;
  useshelles = 0;
  epsilon = 1.0e-8;
  epsilon2 = 1.0e-5;
  object = NONE;
  // Initialize strings
  commandline[0] = '\0';
  infilename[0] = '\0';
  outfilename[0] = '\0';
  addinfilename[0] = '\0';
  bgmeshfilename[0] = '\0';
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// versioninfo()    Print the version information of TetGen.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::versioninfo()
{
  printf("Version 1.4.2 (April 16, 2007).\n");
  printf("\n");
  printf("Copyright (C) 2002 - 2007\n");
  printf("Hang Si\n");
  printf("Mohrenstr. 39, 10117 Berlin, Germany\n");
  printf("si@wias-berlin.de\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// syntax()    Print list of command line switches and exit the program.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::syntax()
{
  printf("  tetgen [-prq_Ra_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n");
  printf("    -p  Tetrahedralizes a piecewise linear complex (PLC).\n");
  printf("    -r  Reconstructs a previously generated mesh.\n");
  printf("    -q  Quality mesh generation (adding new mesh points to ");
  printf("improve mesh quality).\n");
  printf("    -R  Mesh coarsening (deleting redundant mesh points).\n");
  printf("    -a  Applies a maximum tetrahedron volume constraint.\n");
  printf("    -A  Assigns attributes to identify tetrahedra in different ");
  printf("regions.\n");
  printf("    -i  Inserts a list of additional points into mesh.\n");
  printf("    -M  Does not merge coplanar facets.\n");
  printf("    -Y  Suppresses boundary facets/segments splitting.\n");
  printf("    -S  Specifies maximum number of added points.\n");
  printf("    -T  Sets a tolerance for coplanar test (default 1e-8).\n");
  printf("    -d  Detects self-intersections of facets of the PLC.\n");
  printf("    -z  Numbers all output items starting from zero.\n");
  printf("    -o2 Generates second-order subparametric elements.\n");
  printf("    -f  Outputs all faces to .face file.");
  printf("file.\n");
  printf("    -e  Outputs all edges to .edge file.\n");
  printf("    -n  Outputs tetrahedra neighbors to .neigh file.\n");
  printf("    -v  Outputs Voronoi diagram to files.\n");
  printf("    -g  Outputs mesh to .mesh file for viewing by Medit.\n");
  printf("    -G  Outputs mesh to .msh file for viewing by Gid.\n");
  printf("    -O  Outputs mesh to .off file for viewing by Geomview.\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 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");
  versioninfo();
  printf("\n");
  printf("What Can TetGen Do?\n");
  printf("\n");
  printf("  TetGen generates exact Delaunay tetrahedralizations, exact\n");
  printf("  constrained Delaunay tetrahedralizations, and quality ");
  printf("tetrahedral\n  meshes. The latter are nicely graded and whose ");
  printf("tetrahedra have\n  radius-edge ratio bounded, thus are suitable ");
  printf("for finite element and\n  finite volume analysis.\n"); 
  printf("\n");
  printf("Command Line Syntax:\n");
  printf("\n");
  printf("  Below is the command line syntax of TetGen with a list of ");
  printf("short\n");
  printf("  descriptions. Underscores indicate that numbers may optionally\n");
  printf("  follow certain switches.  Do not leave any space between a ");
  printf("switch\n");
  printf("  and its numeric parameter.  \'input_file\' contains input data\n");
  printf("  depending on the switches you supplied which may be a ");
  printf("  piecewise\n");
  printf("  linear complex or a list of nodes.  File formats and detailed\n");
  printf("  description of command line switches are found in user's ");
  printf("manual.\n");
  printf("\n");
  syntax();
  printf("\n");
  printf("Examples of How to Use TetGen:\n");
  printf("\n");
  printf("  \'tetgen object\' reads vertices from object.node, and writes ");
  printf("their\n  Delaunay tetrahedralization to object.1.node and ");
  printf("object.1.ele.\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 and ");
  printf("object.1.face.\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  and object.1.face.\n");
  printf("\n");
  printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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.                           //
//                                                                           //
// When TetGen is invoked from an environment. 'argc' is nonzero, switches   //
// and input filename should be supplied as zero-terminated strings in       //
// argv[0] through argv[argc - 1] and argv[0] shall be the name used to      //
// invoke TetGen, i.e. "tetgen".  Switches are previously started with a     //
// dash '-' to identify them from the input filename.                        //
//                                                                           //
// When TetGen is called from within another program. 'argc' is set to zero. //
// switches are given in one zero-terminated string (no previous dash is     //
// required.), and 'argv' is a pointer points to this string.  No input      //
// filename is required (usually the input data has been directly created by //
// user in the 'tetgenio' structure).  A default filename 'tetgen-tmpfile'   //
// will be created for debugging output purpose.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenbehavior::parse_commandline(int argc, char **argv)
{
  int startindex;
  int increment;
  int meshnumber;
  int scount;
  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, " ");
  }
  
  // Rcount used to count the number of '-R' be used.
  scount = 0;

  for (i = startindex; i < argc; i++) {
    // Remember the command line switches.
    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';
        // Go to the next string directly.
        continue;                     
      }
    }
    // Parse the individual switch from the string.
    for (j = startindex; argv[i][j] != '\0'; j++) {
      if (argv[i][j] == 'p') {
        plc = 1;
      } else if (argv[i][j] == 'r') {
        refine = 1;
      } else if (argv[i][j] == 'R') {
        coarse = 1;
      } else if (argv[i][j] == 'q') {
        quality++;
        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';
          if (quality == 1) {
            minratio = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 2) {
            mindihedral = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 3) {
            maxdihedral = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 4) {
            alpha2 = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 5) {
            alpha1 = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } else if (argv[i][j] == 'm') {
        metric++;
        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';
          if (metric == 1) {
            alpha1 = (REAL) strtod(workstring, (char **) NULL);
          } else if (metric == 2) {
            alpha2 = (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);
        } else {
          varvolume = 1;
        }
      } else if (argv[i][j] == 'A') {
        regionattrib++;
      } else if (argv[i][j] == 'i') {
        insertaddpoints = 1;
      } else if (argv[i][j] == 'd') {
        diagnose = 1;
      } else if (argv[i][j] == 'z') {
        zeroindex = 1;
      } else if (argv[i][j] == 'f') {
        facesout = 1;
      } else if (argv[i][j] == 'e') {
        edgesout++;
      } else if (argv[i][j] == 'n') {
        neighout++;
      } else if (argv[i][j] == 'v') {
        voroout = 1;
      } else if (argv[i][j] == 'g') {
        meditview = 1;
      } else if (argv[i][j] == 'G') {
        gidview = 1;
      } else if (argv[i][j] == 'O') {
        geomview = 1;
      } else if (argv[i][j] == 'M') {
        nomerge = 1;
      } else if (argv[i][j] == 'Y') {
        nobisect++;
      } 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] == 'o') {
        if (argv[i][j + 1] == '2') {
          j++;
          order = 2;
        }
      } 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';
          steiner = (int) strtol(workstring, (char **) NULL, 0);
        } 
      } else if (argv[i][j] == 's') {
        scount++;
        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';
          if (scount == 1) {
            optlevel = (int) strtol(workstring, (char **) NULL, 0);
          } else if (scount == 2) {
            optpasses = (int) strtol(workstring, (char **) NULL, 0);
          }
        }
      } else if (argv[i][j] == 'D') {
        conformdel++;
      } 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] == 'X') {
        fliprepair = 0;
      } else if (argv[i][j] == 'Q') {
        quiet = 1;
      } else if (argv[i][j] == 'V') {
        verbose++;
      // } else if (argv[i][j] == 'v') {
        // versioninfo();
        // terminatetetgen(0);
      } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
                 (argv[i][j] == '?')) {
        usage();
        terminatetetgen(0);
      } else {
        printf("Warning:  Unknown switch -%c.\n", argv[i][j]);
      }
    }
  }

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

  // Detect improper combinations of switches.
  if (plc && refine) {
    printf("Error:  Switch -r cannot use together with -p.\n");
    return false;
  }
  if (refine && (plc || noiterationnum)) {
    printf("Error:  Switches %s cannot use together with -r.\n",
           "-p, -d, and -I");
    return false;
  }
  if (diagnose && (quality || insertaddpoints || (order == 2) || neighout
      || docheck)) {
    printf("Error:  Switches %s cannot use together with -d.\n",
           "-q, -i, -o2, -n, and -C");
    return false;
  }

  // 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;
  }
  // 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;
  }
  // If '-a' or '-aa' is in use, enable '-q' option too.
  if (fixedvolume || varvolume) {
    if (quality == 0) {
      quality = 1;
    }
  }
  // Calculate the goodangle for testing bad subfaces.
  goodangle = cos(minangle * PI / 180.0);
  goodangle *= goodangle;

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

//
// End of class 'tetgenbehavior' implementation
//

//
// Begin of class 'tetgenmesh' implementation
//

//
// Begin of class 'list', 'memorypool' and 'link' implementation
//

// Following are predefined compare functions for primitive data types. 
//   These functions take two pointers of the corresponding date type,
//   perform the comparation. Return -1, 0 or 1 indicating the default
//   linear order of two operators.

// Compare two 'integers'.
int tetgenmesh::compare_2_ints(const void* x, const void* y) {
  if (* (int *) x < * (int *) y) {
    return -1;
  } else if (* (int *) x > * (int *) y) {
    return 1;
  } else {
    return 0;
  }
}

// Compare two 'longs'.  Note: in 64-bit machine the 'long' type is 64-bit
//   (8-byte) where the 'int' only 32-bit (4-byte).
int tetgenmesh::compare_2_longs(const void* x, const void* y) {
  if (* (long *) x < * (long *) y) {
    return -1;
  } else if (* (long *) x > * (long *) y) {
    return 1;
  } else {
    return 0;
  }
}

// Compare two 'unsigned longs'.
int tetgenmesh::compare_2_unsignedlongs(const void* x, const void* y) {
  if (* (unsigned long *) x < * (unsigned long *) y) {
    return -1;
  } else if (* (unsigned long *) x > * (unsigned long *) y) {
    return 1;
  } else {
    return 0;
  }
}

// Compare two 'size types'.
int tetgenmesh::compare_2_sizets(const void* x, const void* y) {
  if (* (size_t *) x < * (size_t *) y) {
    return -1;
  } else if (* (size_t *) x > * (size_t *) y) {
    return 1;
  } else {
    return 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// set_compfunc()    Determine the size of primitive data types and set the  //
//                   corresponding predefined linear order functions.        //
//                                                                           //
// 'str' is a zero-end string indicating a primitive data type, like 'int',  //
// 'long' or 'unsigned long'.  Every string ending with a '*' is though as a //
// type of pointer and the type 'unsign long' is used for it.                //
//                                                                           //
// When the type of 'str' is determined, the size of this type (in byte) is  //
// returned in 'itbytes', and the pointer of corresponding predefined linear //
// order functions is returned in 'pcomp'.                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::set_compfunc(char* str, int* itbytes, compfunc* pcomp)
{
  // First figure out whether it is a pointer or not.
  if (str[strlen(str) - 1] == '*') {
    *itbytes = sizeof(size_t);
    *pcomp = &compare_2_sizets;
    return;
  }
  // Then determine other types.
  if (strcmp(str, "int") == 0) {
    *itbytes = sizeof(int);
    *pcomp = &compare_2_ints;
  } else if (strcmp(str, "long") == 0) {
    *itbytes = sizeof(long);
    *pcomp = &compare_2_longs;
  } else if (strcmp(str, "unsigned long") == 0) {
    *itbytes = sizeof(unsigned long);
    *pcomp = &compare_2_unsignedlongs;
  } else {
    // It is an unknown type.
    printf("Error in set_compfunc():  unknown type %s.\n", str);
    terminatetetgen(1);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// listinit()    Initialize a list for storing a data type.                  //
//                                                                           //
// Determine the size of each item, set the maximum size allocated at onece, //
// set the expand size in case the list is full, and set the linear order    //
// function if it is provided (default is NULL).                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::list::
listinit(int itbytes, compfunc pcomp, int mitems,int exsize)
{
#ifdef SELF_CHECK
  assert(itbytes > 0 && mitems > 0 && exsize > 0);
#endif
  itembytes = itbytes;
  comp = pcomp;
  maxitems = mitems;
  expandsize = exsize;
  base = (char *) malloc(maxitems * itembytes); 
  if (base == (char *) NULL) {
    printf("Error:  Out of memory.\n");
    terminatetetgen(1);
  }
  items = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// append()    Add a new item at the end of the list.                        //
//                                                                           //
// A new space at the end of this list will be allocated for storing the new //
// item. If the memory is not sufficient, reallocation will be performed. If //
// 'appitem' is not NULL, the contents of this pointer will be copied to the //
// new allocated space.  Returns the pointer to the new allocated space.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::list::append(void *appitem)
{
  // Do we have enough space?
  if (items == maxitems) {
    char* newbase = (char *) realloc(base, (maxitems + expandsize) * 
                                     itembytes);
    if (newbase == (char *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    base = newbase;
    maxitems += expandsize;
  }
  if (appitem != (void *) NULL) {
    memcpy(base + items * itembytes, appitem, itembytes);
  }
  items++;
  return (void *) (base + (items - 1) * itembytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insert()    Insert an item before 'pos' (range from 0 to items - 1).      //
//                                                                           //
// A new space will be inserted at the position 'pos', that is, items lie    //
// after pos (including the item at pos) will be moved one space downwords.  //
// If 'insitem' is not NULL, its contents will be copied into the new        //
// inserted space. Return a pointer to the new inserted space.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::list::insert(int pos, void* insitem)
{
  if (pos >= items) {
    return append(insitem);
  }
  // Do we have enough space.
  if (items == maxitems) {
    char* newbase = (char *) realloc(base, (maxitems + expandsize) *
                                     itembytes);
    if (newbase == (char *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    base = newbase;
    maxitems += expandsize;
  }
  // Do block move.
  memmove(base + (pos + 1) * itembytes,   // dest
          base + pos * itembytes,         // src
          (items - pos) * itembytes);     // size in bytes
  // Insert the item.
  if (insitem != (void *) NULL) {
    memcpy(base + pos * itembytes, insitem, itembytes);
  }
  items++;
  return (void *) (base + pos * itembytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// del()    Delete an item at 'pos' (range from 0 to items - 1).             //
//                                                                           //
// The space at 'pos' will be overlapped by other item. If 'order' is 1, the //
// remaining items of the list have the same order as usual, i.e., items lie //
// after pos will be moved one space upwords. If 'order' is 0, the last item //
// of the list will be moved up to pos.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::list::del(int pos, int order)
{
  // If 'pos' is the last item of the list, nothing need to do.
  if (pos >= 0 && pos < items - 1) {
    if (order == 1) {
      // Do block move. 
      memmove(base + pos * itembytes,       // dest
              base + (pos + 1) * itembytes, // src
              (items - pos - 1) * itembytes);
    } else {
      // Use the last item to overlap the del item.
      memcpy(base + pos * itembytes, // item at pos
             base + (items - 1) * itembytes, // item at last
             itembytes);
    }
  }
  if (items > 0) {
    items--;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hasitem()    Search in this list to find if 'checkitem' exists.           //
//                                                                           //
// This routine assumes that a linear order function has been set.  It loops //
// through the entire list, compares each item to 'checkitem'. If it exists, //
// return its position (between 0 to items - 1), otherwise, return -1.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::list::hasitem(void* checkitem)
{
  int i;

  for (i = 0; i < items; i++) {
    if (comp != (compfunc) NULL) {
      if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) {
        return i;
      }
    }
  }
  return -1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sort()    Sort the items with respect to a linear order function.         //
//                                                                           //
// Uses QuickSort routines (qsort) of the standard C/C++ library (stdlib.h). //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::list::sort()
{
  qsort((void *) base, (size_t) items, (size_t) itembytes, comp);
}

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

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

tetgenmesh::memorypool::
memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment)
{
  poolinit(bytecount, itemcount, wtype, 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, enum wordtype wtype, int alignment)
{
  int wordsize;

  // Initialize values in the pool.
  itemwordtype = wtype;
  wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL);
  // 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) {
    printf("Error:  Out of memory.\n");
    terminatetetgen(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()
{
  size_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 = (size_t) (nowblock + 1);
  // Align the item on an `alignbytes'-byte boundary.
  nextitem = (void *)
    (alignptr + (size_t) alignbytes -
     (alignptr % (size_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;
  size_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) {
          printf("Error:  Out of memory.\n");
          terminatetetgen(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 = (size_t) (nowblock + 1);
      // Align the item on an `alignbytes'-byte boundary.
      nextitem = (void *)
        (alignptr + (size_t) alignbytes -
         (alignptr % (size_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.
    if (itemwordtype == POINTER) {
      nextitem = (void *) ((void **) nextitem + itemwords);
    } else {
      nextitem = (void *) ((REAL *) nextitem + itemwords);
    }
    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()
{
  size_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 = (size_t) (pathblock + 1);
  // Align with item on an `alignbytes'-byte boundary.
  pathitem = (void *)
    (alignptr + (size_t) alignbytes -
     (alignptr % (size_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;
  size_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 = (size_t) (pathblock + 1);
    // Align with item on an `alignbytes'-byte boundary.
    pathitem = (void *)
      (alignptr + (size_t) alignbytes -
       (alignptr % (size_t) alignbytes));
    // Set the number of items left in the current block.
    pathitemsleft = itemsperblock;
  }
  newitem = pathitem;
  // Find the next item in the block.
  if (itemwordtype == POINTER) {
    pathitem = (void *) ((void **) pathitem + itemwords);
  } else {
    pathitem = (void *) ((REAL *) pathitem + itemwords);
  }
  pathitemsleft--;
  return newitem;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// linkinit()    Initialize a link for storing items.                        //
//                                                                           //
// The input parameters are the size of each item, a pointer of a linear     //
// order function and the number of items allocating in one memory bulk.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::link::linkinit(int bytecount, compfunc pcomp, int itemcount)
{
#ifdef SELF_CHECK
  assert(bytecount > 0 && itemcount > 0);
#endif
  // Remember the real size of each item.
  linkitembytes = bytecount;
  // Set the linear order function for this link.
  comp = pcomp;

  // Call the constructor of 'memorypool' to initialize its variables.
  //   like: itembytes, itemwords, items, ... Each node has size
  //   bytecount + 2 * sizeof(void **), and total 'itemcount + 2' (because
  //   link has additional two nodes 'head' and 'tail').
  poolinit(bytecount + 2 * sizeof(void **), itemcount + 2, POINTER, 0);
  
  // Initial state of this link.
  head = (void **) alloc();
  tail = (void **) alloc();
  *head = (void *) tail;
  *(head + 1) = NULL;
  *tail = NULL;
  *(tail + 1) = (void *) head;
  nextlinkitem = *head;
  curpos = 1;
  linkitems = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// clear()   Deallocate all nodes in this link.                              //
//                                                                           //
// The link 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::link::clear()
{
  // Reset the pool.
  restart();

  // Initial state of this link.
  head = (void **) alloc();
  tail = (void **) alloc();
  *head = (void *) tail;
  *(head + 1) = NULL;
  *tail = NULL;
  *(tail + 1) = (void *) head;
  nextlinkitem = *head;
  curpos = 1;
  linkitems = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// move()    Causes 'nextlinkitem' to traverse the specified number of nodes,//
//           updates 'curpos' to be the node to which 'nextlinkitem' points. //
//                                                                           //
// 'numberofnodes' is a number indicating how many nodes need be traversed   //
// (not counter the current node) need be traversed. It may be positive(move //
// forward) or negative (move backward).  Return TRUE if it is successful.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::link::move(int numberofnodes)
{
  void **nownode;
  int i;

  nownode = (void **) nextlinkitem;
  if (numberofnodes > 0) {
    // Move forward.
    i = 0;
    while ((i < numberofnodes) && *nownode) {
      nownode = (void **) *nownode;
      i++;
    }
    if (*nownode == NULL) return false;
    nextlinkitem = (void *) nownode;
    curpos += numberofnodes;
  } else if (numberofnodes < 0) {
    // Move backward.
    i = 0;
    numberofnodes = -numberofnodes;
    while ((i < numberofnodes) && *(nownode + 1)) {
      nownode = (void **) *(nownode + 1);
      i++;
    }
    if (*(nownode + 1) == NULL) return false;
    nextlinkitem = (void *) nownode;
    curpos -= numberofnodes;
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locate()    Locates the node at the specified position.                   //
//                                                                           //
// The number 'pos' (between 1 and 'linkitems') indicates the location. This //
// routine first decides the shortest path traversing from 'curpos' to 'pos',//
// i.e., from head, tail or 'curpos'.   Routine 'move()' is called to really //
// traverse the link. If success, 'nextlinkitem' points to the node, 'curpos'//
// and 'pos' are equal. Otherwise, return FALSE.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::link::locate(int pos)
{
  int headdist, taildist, curdist;
  int abscurdist, mindist;

  if (pos < 1 || pos > linkitems) return false;

  headdist = pos - 1;
  taildist = linkitems - pos;
  curdist = pos - curpos;
  abscurdist = curdist >= 0 ? curdist : -curdist;

  if (headdist > taildist) {
    if (taildist > abscurdist) {
      mindist = curdist;
    } else {
      // taildist <= abs(curdist)
      mindist = -taildist;
      goend();
    }
  } else {
    // headdist <= taildist
    if (headdist > abscurdist) {
      mindist = curdist;
    } else {
      // headdist <= abs(curdist)
      mindist = headdist;
      rewind();
    }
  }

  return move(mindist);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// add()    Add a node at the end of this link.                              //
//                                                                           //
// A new node is appended to the end of the link.  If 'newitem' is not NULL, //
// its conents will be copied to the data slot of the new node. Returns the  //
// pointer to the newest added node.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::add(void* newitem)
{
  void **newnode = tail;
  if (newitem != (void *) NULL) {
    memcpy((void *)(newnode + 2), newitem, linkitembytes);
  }
  tail = (void **) alloc();
  *tail = NULL;
  *newnode = (void*) tail;
  *(tail + 1) = (void*) newnode;
  linkitems++;
  return (void *)(newnode + 2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insert()    Inserts a node before the specified position.                 //
//                                                                           //
// 'pos' (between 1 and 'linkitems') indicates the inserting position.  This //
// routine inserts a new node before the node of 'pos'.  If 'newitem' is not //
// NULL,  its conents will be copied into the data slot of the new node.  If //
// 'pos' is larger than 'linkitems', it is equal as 'add()'.  A pointer to   //
// the newest inserted item is returned.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::insert(int pos, void* insitem)
{
  if (!locate(pos)) {
    return add(insitem);
  }

  void **nownode = (void **) nextlinkitem;

  // Insert a node before 'nownode'.
  void **newnode = (void **) alloc();
  if (insitem != (void *) NULL) {
    memcpy((void *)(newnode + 2), insitem, linkitembytes);
  }

  *(void **)(*(nownode + 1)) = (void *) newnode;
  *newnode = (void *) nownode;
  *(newnode + 1) = *(nownode + 1);
  *(nownode + 1) = (void *) newnode;

  linkitems++;

  nextlinkitem = (void *) newnode;
  return (void *)(newnode + 2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// del()    Delete a node.                                                   //
//                                                                           //
// Returns a pointer of the deleted data. If you try to delete a non-existed //
// node (e.g. link is empty or a wrong index is given) return NULL.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::deletenode(void** deadnode)
{
  void **nextnode = (void **) *deadnode;
  void **prevnode = (void **) *(deadnode + 1);
  *prevnode = (void *) nextnode;
  *(nextnode + 1) = (void *) prevnode;

  dealloc((void *) deadnode);
  linkitems--;

  nextlinkitem = (void *) nextnode;
  return (void *)(deadnode + 2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// del()    Delete a node at the specified position.                         //
//                                                                           //
// 'pos' between 1 and 'linkitems'.  Returns a pointer of the deleted data.  //
// If you try to delete a non-existed node (e.g. link is empty or a wrong    //
// index is given) return NULL.                                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::del(int pos)
{
  if (!locate(pos) || (linkitems == 0)) {
    return (void *) NULL;
  }
  return deletenode((void **) nextlinkitem);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getitem()    The link traversal routine.                                  //
//                                                                           //
// Returns the node to which 'nextlinkitem' points. Returns a 'NULL' if the  //
// end of the link is reaching.  Both 'nextlinkitem' and 'curpos' will be    //
// updated after this operation.                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::getitem()
{
  if (nextlinkitem == (void *) tail) return NULL;
  void **nownode = (void **) nextlinkitem;
  nextlinkitem = *nownode;
  curpos += 1;
  return (void *)(nownode + 2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getnitem()    Returns the node at a specified position.                   //
//                                                                           //
// 'pos' between 1 and 'linkitems'. After this operation, 'nextlinkitem' and //
// 'curpos' will be updated to indicate this node.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::link::getnitem(int pos)
{
  if (!locate(pos)) return NULL;
  return (void *)((void **) nextlinkitem + 2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hasitem()    Search in this link to find if 'checkitem' exists.           //
//                                                                           //
// If 'checkitem' exists, return its position (between 1 to 'linkitems'),    //
// otherwise, return -1. This routine requires the linear order function has //
// been set.                                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::link::hasitem(void* checkitem)
{
  void *pathitem;
  int count;

  rewind();
  pathitem = getitem();
  count = 0;
  while (pathitem) {
    count ++;
    if (comp) {
      if ((* comp)(pathitem, checkitem) == 0) {
        return count;
      }
    } 
    pathitem = getitem();
  }
  return -1;
}

//
// End of class 'list', 'memorypool' and 'link' implementation
//

//
// Begin of mesh manipulation primitives
//

//
// Begin of tables initialization.
//

// For enumerating three edges of a triangle.

int tetgenmesh::plus1mod3[3] = {1, 2, 0};
int tetgenmesh::minus1mod3[3] = {2, 0, 1};

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

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

// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of
//   the origin, destination and apex in the triangle.

int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 };
int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 };
int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 };

// The following tables are for tetrahedron primitives (operate on trifaces).

// For 'org()', 'dest()' and 'apex()'.  Use 'loc' as the first index and
//   'ver' as the second index.

int tetgenmesh::locver2org[4][6]  = {
  {0, 1, 1, 2, 2, 0},
  {0, 3, 3, 1, 1, 0},
  {1, 3, 3, 2, 2, 1},
  {2, 3, 3, 0, 0, 2} 
};
int tetgenmesh::locver2dest[4][6] = { 
  {1, 0, 2, 1, 0, 2},
  {3, 0, 1, 3, 0, 1},
  {3, 1, 2, 3, 1, 2},
  {3, 2, 0, 3, 2, 0}
};
int tetgenmesh::locver2apex[4][6] = { 
  {2, 2, 0, 0, 1, 1},
  {1, 1, 0, 0, 3, 3},
  {2, 2, 1, 1, 3, 3},
  {0, 0, 2, 2, 3, 3}
};

// For oppo() primitives, use 'loc' as the index.

int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 };

// For fnext() primitive.  Use 'loc' as the first index and 'ver' as the
//   second index. Returns a new 'loc' and new 'ver' in an array. (It is
//   only valid for edge version equals one of {0, 2, 4}.)

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

// The edge number (from 0 to 5) of a tet is defined as follows:
//   0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0)
//   3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2).

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

int tetgenmesh::edge2locver[6][2] = {
  {0, 0}, // 0  v0 -> v1
  {0, 2}, // 1  v1 -> v2
  {0, 4}, // 2  v2 -> v1
  {1, 0}, // 3  v0 -> v3
  {1, 2}, // 4  v1 -> v3
  {2, 2}  // 5  v2 -> v3
};

//
// End of tables initialization.
//

// Some macros for convenience

#define Div2  >> 1
#define Mod2  & 01

// NOTE: These bit operators should only be used in macros below.

// Get orient(Range from 0 to 2) from face version(Range from 0 to 5).

#define Orient(V)   ((V) Div2)

// Determine edge ring(0 or 1) from face version(Range from 0 to 5).

#define EdgeRing(V) ((V) Mod2)

//
// Begin of primitives for tetrahedra
// 

// Each tetrahedron contains four pointers to its neighboring tetrahedra,
//   with face indices.  To save memory, both information are kept in a
//   single pointer. To make this possible, all tetrahedra are aligned to
//   eight-byte boundaries, so that the last three bits of each pointer are
//   zeros. A face index (in the range 0 to 3) is compressed into the last
//   two bits of each pointer by the function 'encode()'.  The function
//   'decode()' decodes a pointer, extracting a face index and a pointer to
//   the beginning of a tetrahedron.

inline void tetgenmesh::decode(tetrahedron ptr, triface& t) {
  t.loc = (int) ((size_t) (ptr) & (size_t) 3l);
  t.tet = (tetrahedron *) ((size_t) (ptr) & ~(size_t) 7l);
}

inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) {
  return (tetrahedron) ((size_t) t.tet | (size_t) t.loc);
}

// sym() finds the abutting tetrahedron on the same face.

inline void tetgenmesh::sym(triface& t1, triface& t2) {
  tetrahedron ptr = t1.tet[t1.loc];
  decode(ptr, t2);
}

inline void tetgenmesh::symself(triface& t) {
  tetrahedron ptr = t.tet[t.loc];
  decode(ptr, t);
}

// Bond two tetrahedra together at their faces.

inline void tetgenmesh::bond(triface& t1, triface& t2) {
  t1.tet[t1.loc] = encode(t2);
  t2.tet[t2.loc] = encode(t1);
}

// Dissolve a bond (from one side).  Note that the other tetrahedron will
//   still think it is connected to this tetrahedron.  Usually, however,
//   the other tetrahedron is being deleted entirely, or bonded to another
//   tetrahedron, so it doesn't matter.

inline void tetgenmesh::dissolve(triface& t) {
  t.tet[t.loc] = (tetrahedron) dummytet;
}

// These primitives determine or set the origin, destination, apex or
//   opposition of a tetrahedron with respect to 'loc' and 'ver'.

inline tetgenmesh::point tetgenmesh::org(triface& t) {
  return (point) t.tet[locver2org[t.loc][t.ver] + 4];
}

inline tetgenmesh::point tetgenmesh::dest(triface& t) {
  return (point) t.tet[locver2dest[t.loc][t.ver] + 4];
}

inline tetgenmesh::point tetgenmesh::apex(triface& t) {
  return (point) t.tet[locver2apex[t.loc][t.ver] + 4];
}

inline tetgenmesh::point tetgenmesh::oppo(triface& t) {
  return (point) t.tet[loc2oppo[t.loc] + 4];
}

inline void tetgenmesh::setorg(triface& t, point pointptr) {
  t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
}

inline void tetgenmesh::setdest(triface& t, point pointptr) {
  t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
}

inline void tetgenmesh::setapex(triface& t, point pointptr) {
  t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
}

inline void tetgenmesh::setoppo(triface& t, point pointptr) {
  t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr;
}

// These primitives were drived from Mucke's triangle-edge data structure
//   to change face-edge relation in a tetrahedron (esym, enext and enext2)
//   or between two tetrahedra (fnext).

// If e0 = e(i, j), e1 = e(j, i), that is e0 and e1 are the two directions
//   of the same undirected edge of a face. e0.sym() = e1 and vice versa.

inline void tetgenmesh::esym(triface& t1, triface& t2) {
  t2.tet = t1.tet;
  t2.loc = t1.loc;
  t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1);
}

inline void tetgenmesh::esymself(triface& t) {
  t.ver += (EdgeRing(t.ver) ? -1 : 1);
}

// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext().

inline void tetgenmesh::enext(triface& t1, triface& t2) {
  t2.tet = t1.tet;
  t2.loc = t1.loc;
  t2.ver = ve[t1.ver];
}

inline void tetgenmesh::enextself(triface& t) {
  t.ver = ve[t.ver];
}

// enext2() is equal to e2 = e0.enext().enext()

inline void tetgenmesh::enext2(triface& t1, triface& t2) {
  t2.tet = t1.tet;
  t2.loc = t1.loc;
  t2.ver = ve[ve[t1.ver]];
}

inline void tetgenmesh::enext2self(triface& t) {
  t.ver = ve[ve[t.ver]];
}

// If f0 and f1 are both in the same face ring of a face, f1 = f0.fnext().
//   If f1 exists, return true. Otherwise, return false, i.e., f0 is a
//   boundary or hull face.

inline bool tetgenmesh::fnext(triface& t1, triface& t2) 
{
  // Get the next face.
  t2.loc = locver2nextf[t1.loc][t1.ver][0];
  // Is the next face in the same tet?
  if (t2.loc != -1) {
    // It's in the same tet. Get the edge version.
    t2.ver = locver2nextf[t1.loc][t1.ver][1];
    t2.tet = t1.tet;
  } else {
    // The next face is in the neigbhour of 't1'.
    sym(t1, t2);
    if (t2.tet != dummytet) {
      // Find the corresponding edge in t2.
      point torg;
      int tloc, tver, i;
      t2.ver = 0;
      torg = org(t1);
      for (i = 0; (i < 3) && (org(t2) != torg); i++) {
        enextself(t2);
      }
      // Go to the next face in t2.
      tloc = t2.loc;
      tver = t2.ver;
      t2.loc = locver2nextf[tloc][tver][0];
      t2.ver = locver2nextf[tloc][tver][1];
    }
  }
  return t2.tet != dummytet;
}

inline bool tetgenmesh::fnextself(triface& t1) 
{
  triface t2;

  // Get the next face.
  t2.loc = locver2nextf[t1.loc][t1.ver][0];
  // Is the next face in the same tet?
  if (t2.loc != -1) {
    // It's in the same tet. Get the edge version.
    t2.ver = locver2nextf[t1.loc][t1.ver][1];
    t1.loc = t2.loc;
    t1.ver = t2.ver;
  } else {
    // The next face is in the neigbhour of 't1'.
    sym(t1, t2);
    if (t2.tet != dummytet) {
      // Find the corresponding edge in t2.
      point torg;
      int i;
      t2.ver = 0;
      torg = org(t1);
      for (i = 0; (i < 3) && (org(t2) != torg); i++) {
        enextself(t2);
      }
      t1.loc = locver2nextf[t2.loc][t2.ver][0];
      t1.ver = locver2nextf[t2.loc][t2.ver][1];
      t1.tet = t2.tet;
    }
  }
  return t2.tet != dummytet;
}

// enextfnext() and enext2fnext() are combination primitives of enext(),
//   enext2() and fnext().

inline void tetgenmesh::enextfnext(triface& t1, triface& t2) {
  enext(t1, t2);
  fnextself(t2);
}

inline void tetgenmesh::enextfnextself(triface& t) {
  enextself(t);
  fnextself(t);
}

inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) {
  enext2(t1, t2);
  fnextself(t2);
}

inline void tetgenmesh::enext2fnextself(triface& t) {
  enext2self(t);
  fnextself(t);
}

// Primitives to infect or cure a tetrahedron with the virus.   The last
//   third bit of the pointer is marked for infection.  These rely on the
//   assumption that all tetrahedron are aligned to eight-byte boundaries.

inline void tetgenmesh::infect(triface& t) {
  t.tet[0] = (tetrahedron) ((size_t) t.tet[0] | (size_t) 4l);
}

inline void tetgenmesh::uninfect(triface& t) {
  t.tet[0] = (tetrahedron) ((size_t) t.tet[0] & ~ (size_t) 4l);
}

// Test a tetrahedron for viral infection.

inline bool tetgenmesh::infected(triface& t) {
  return (((size_t) t.tet[0] & (size_t) 4l) != 0);
}

// Check or set a tetrahedron's attributes.

inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) {
  return ((REAL *) (ptr))[elemattribindex + attnum];
}

inline void tetgenmesh::
setelemattribute(tetrahedron* ptr, int attnum, REAL value){
  ((REAL *) (ptr))[elemattribindex + attnum] = value;
}

// Check or set a tetrahedron's maximum volume bound.

inline REAL tetgenmesh::volumebound(tetrahedron* ptr) {
  return ((REAL *) (ptr))[volumeboundindex];
}

inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) {
  ((REAL *) (ptr))[volumeboundindex] = value;
}

//
// End of primitives for tetrahedra
//

//
// Begin of primitives for subfaces/subsegments
//

// Each subface contains three pointers to its neighboring subfaces, with
//   edge versions.  To save memory, both information are kept in a single
//   pointer. To make this possible, all subfaces are aligned to eight-byte
//   boundaries, so that the last three bits of each pointer are zeros. An
//   edge version (in the range 0 to 5) is compressed into the last three
//   bits of each pointer by 'sencode()'.  'sdecode()' decodes a pointer,
//   extracting an edge version and a pointer to the beginning of a subface.

inline void tetgenmesh::sdecode(shellface sptr, face& s) {
  s.shver = (int) ((size_t) (sptr) & (size_t) 7l);
  s.sh = (shellface *) ((size_t) (sptr) & ~ (size_t) 7l);
}

inline tetgenmesh::shellface tetgenmesh::sencode(face& s) {
  return (shellface) ((size_t) s.sh | (size_t) s.shver);
}

// spivot() finds the other subface (from this subface) that shares the
//   same edge.

inline void tetgenmesh::spivot(face& s1, face& s2) {
  shellface sptr = s1.sh[Orient(s1.shver)];
  sdecode(sptr, s2);
}

inline void tetgenmesh::spivotself(face& s) {
  shellface sptr = s.sh[Orient(s.shver)];
  sdecode(sptr, s);
}

// sbond() bonds two subfaces together, i.e., after bonding, both faces
//   are pointing to each other.

inline void tetgenmesh::sbond(face& s1, face& s2) {
  s1.sh[Orient(s1.shver)] = sencode(s2);
  s2.sh[Orient(s2.shver)] = sencode(s1);
}

// sbond1() only bonds s2 to s1, i.e., after bonding, s1 is pointing to s2,
//   but s2 is not pointing to s1.

inline void tetgenmesh::sbond1(face& s1, face& s2) {
  s1.sh[Orient(s1.shver)] = sencode(s2);
}

// Dissolve a subface bond (from one side).  Note that the other subface
//   will still think it's connected to this subface.

inline void tetgenmesh::sdissolve(face& s) {
  s.sh[Orient(s.shver)] = (shellface) dummysh;
}

// These primitives determine or set the origin, destination, or apex
//   of a subface with respect to the edge version.

inline tetgenmesh::point tetgenmesh::sorg(face& s) {
  return (point) s.sh[3 + vo[s.shver]];
}

inline tetgenmesh::point tetgenmesh::sdest(face& s) {
  return (point) s.sh[3 + vd[s.shver]];
}

inline tetgenmesh::point tetgenmesh::sapex(face& s) {
  return (point) s.sh[3 + va[s.shver]];
}

inline void tetgenmesh::setsorg(face& s, point pointptr) {
  s.sh[3 + vo[s.shver]] = (shellface) pointptr;
}

inline void tetgenmesh::setsdest(face& s, point pointptr) {
  s.sh[3 + vd[s.shver]] = (shellface) pointptr;
}

inline void tetgenmesh::setsapex(face& s, point pointptr) {
  s.sh[3 + va[s.shver]] = (shellface) pointptr;
}

// These primitives were drived from Mucke[2]'s triangle-edge data structure
//   to change face-edge relation in a subface (sesym, senext and senext2).

inline void tetgenmesh::sesym(face& s1, face& s2) {
  s2.sh = s1.sh;
  s2.shver = s1.shver + (EdgeRing(s1.shver) ? -1 : 1);
}

inline void tetgenmesh::sesymself(face& s) {
  s.shver += (EdgeRing(s.shver) ? -1 : 1);
}

inline void tetgenmesh::senext(face& s1, face& s2) {
  s2.sh = s1.sh;
  s2.shver = ve[s1.shver];
}

inline void tetgenmesh::senextself(face& s) { 
  s.shver = ve[s.shver]; 
}

inline void tetgenmesh::senext2(face& s1, face& s2) {
  s2.sh = s1.sh;
  s2.shver = ve[ve[s1.shver]];
}

inline void tetgenmesh::senext2self(face& s) {
  s.shver = ve[ve[s.shver]];
}

// If f0 and f1 are both in the same face ring, then f1 = f0.fnext(),

inline void tetgenmesh::sfnext(face& s1, face& s2) {
  getnextsface(&s1, &s2);
}

inline void tetgenmesh::sfnextself(face& s) {
  getnextsface(&s, NULL);
}

// These primitives read or set a pointer of the badface structure.  The
//   pointer is stored sh[11].

inline tetgenmesh::badface* tetgenmesh::shell2badface(face& s) {
  return (badface*) s.sh[11];
}

inline void tetgenmesh::setshell2badface(face& s, badface* value) {
  s.sh[11] = (shellface) value;
}

// Check or set a subface's maximum area bound.

inline REAL tetgenmesh::areabound(face& s) {
  return ((REAL *) (s.sh))[areaboundindex];
}

inline void tetgenmesh::setareabound(face& s, REAL value) {
  ((REAL *) (s.sh))[areaboundindex] = value;
}

// These two primitives read or set a shell marker.  Shell markers are used
//   to hold user boundary information.

inline int tetgenmesh::shellmark(face& s) { 
  return ((int *) (s.sh))[shmarkindex];
}

inline void tetgenmesh::setshellmark(face& s, int value) {
  ((int *) (s.sh))[shmarkindex] = value;
}

// These two primitives set or read the type of the subface or subsegment.

inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) {
  return (enum shestype) ((int *) (s.sh))[shmarkindex + 1];
}

inline void tetgenmesh::setshelltype(face& s, enum shestype value) {
  ((int *) (s.sh))[shmarkindex + 1] = (int) value;
}

// These two primitives set or read the pbc group of the subface.

inline int tetgenmesh::shellpbcgroup(face& s) {
  return ((int *) (s.sh))[shmarkindex + 2];
}

inline void tetgenmesh::setshellpbcgroup(face& s, int value) {
  ((int *) (s.sh))[shmarkindex + 2] = value;
}

// Primitives to infect or cure a subface with the virus. These rely on the
//   assumption that all tetrahedra are aligned to eight-byte boundaries.

inline void tetgenmesh::sinfect(face& s) {
  s.sh[6] = (shellface) ((size_t) s.sh[6] | (size_t) 4l);
}

inline void tetgenmesh::suninfect(face& s) {
  s.sh[6] = (shellface)((size_t) s.sh[6] & ~(size_t) 4l);
}

// Test a subface for viral infection.

inline bool tetgenmesh::sinfected(face& s) {
  return (((size_t) s.sh[6] & (size_t) 4l) != 0);
}

//
// End of primitives for subfaces/subsegments
//

//
// Begin of primitives for interacting between tetrahedra and subfaces
//

// tspivot() finds a subface abutting on this tetrahdera.

inline void tetgenmesh::tspivot(triface& t, face& s) {
  shellface sptr = (shellface) t.tet[8 + t.loc];
  sdecode(sptr, s);
}

// stpivot() finds a tetrahedron abutting a subface.

inline void tetgenmesh::stpivot(face& s, triface& t) {
  tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)];
  decode(ptr, t);
}

// tsbond() bond a tetrahedron to a subface.

inline void tetgenmesh::tsbond(triface& t, face& s) {
  t.tet[8 + t.loc] = (tetrahedron) sencode(s);
  s.sh[6 + EdgeRing(s.shver)] = (shellface) encode(t);
}

// tsdissolve() dissolve a bond (from the tetrahedron side).

inline void tetgenmesh::tsdissolve(triface& t) {
  t.tet[8 + t.loc] = (tetrahedron) dummysh;
}

// stdissolve() dissolve a bond (from the subface side).

inline void tetgenmesh::stdissolve(face& s) {
  s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet;
}

//
// End of primitives for interacting between tetrahedra and subfaces
//

//
// Begin of primitives for interacting between subfaces and subsegs
//

// sspivot() finds a subsegment abutting a subface.

inline void tetgenmesh::sspivot(face& s, face& edge) {
  shellface sptr = (shellface) s.sh[8 + Orient(s.shver)];
  sdecode(sptr, edge);
}

// ssbond() bond a subface to a subsegment.

inline void tetgenmesh::ssbond(face& s, face& edge) {
  s.sh[8 + Orient(s.shver)] = sencode(edge);
  edge.sh[0] = sencode(s);
}

// ssdisolve() dissolve a bond (from the subface side)

inline void tetgenmesh::ssdissolve(face& s) {
  s.sh[8 + Orient(s.shver)] = (shellface) dummysh;
}

//
// End of primitives for interacting between subfaces and subsegs
//

//
// Begin of primitives for interacting between tet and subsegs.
//

inline void tetgenmesh::tsspivot1(triface& t, face& seg)
{
  shellface sptr = (shellface) t.tet[8 + locver2edge[t.loc][t.ver]];
  sdecode(sptr, seg);
}

// Only bond/dissolve at tet's side, but not vice versa.

inline void tetgenmesh::tssbond1(triface& t, face& seg)
{
  t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) sencode(seg);
}

inline void tetgenmesh::tssdissolve1(triface& t)
{
  t.tet[8 + locver2edge[t.loc][t.ver]] = (tetrahedron) dummysh;
}

//
// End of primitives for interacting between tet and subsegs.
//

//
// Begin of primitives for points
//

inline int tetgenmesh::pointmark(point pt) { 
  return ((int *) (pt))[pointmarkindex]; 
}

inline void tetgenmesh::setpointmark(point pt, int value) {
  ((int *) (pt))[pointmarkindex] = value;
}

// These two primitives set and read the type of the point.

inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) {
  return (enum verttype) ((int *) (pt))[pointmarkindex + 1];
}

inline void tetgenmesh::setpointtype(point pt, enum verttype value) {
  ((int *) (pt))[pointmarkindex + 1] = (int) value;
}

// These following primitives set and read a pointer to a tetrahedron
//   a subface/subsegment, a point, or a tet of background mesh.

inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) {
  return ((tetrahedron *) (pt))[point2simindex];
}

inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) {
  ((tetrahedron *) (pt))[point2simindex] = value;
}

inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) {
  return (shellface) ((tetrahedron *) (pt))[point2simindex + 1];
}

inline void tetgenmesh::setpoint2sh(point pt, shellface value) {
  ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value;
}

inline tetgenmesh::point tetgenmesh::point2ppt(point pt) {
  return (point) ((tetrahedron *) (pt))[point2simindex + 2];
}

inline void tetgenmesh::setpoint2ppt(point pt, point value) {
  ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value;
}

inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) {
  return ((tetrahedron *) (pt))[point2simindex + 3];
}

inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) {
  ((tetrahedron *) (pt))[point2simindex + 3] = value;
}

// These primitives set and read a pointer to its pbc point.

inline tetgenmesh::point tetgenmesh::point2pbcpt(point pt) {
  return (point) ((tetrahedron *) (pt))[point2pbcptindex];
}

inline void tetgenmesh::setpoint2pbcpt(point pt, point value) {
  ((tetrahedron *) (pt))[point2pbcptindex] = (tetrahedron) value;
}

//
// End of primitives for points
//

//
// Begin of advanced primitives
//

// adjustedgering() adjusts the edge version so that it belongs to the
//   indicated edge ring.  The 'direction' only can be 0(CCW) or 1(CW).
//   If the edge is not in the wanted edge ring, reverse it.

inline void tetgenmesh::adjustedgering(triface& t, int direction) {
  if (EdgeRing(t.ver) != direction) {
    esymself(t);
  }
}

inline void tetgenmesh::adjustedgering(face& s, int direction) {
  if (EdgeRing(s.shver) != direction) {
    sesymself(s);
  }
}

// isdead() returns TRUE if the tetrahedron or subface has been dealloced.

inline bool tetgenmesh::isdead(triface* t) {
  if (t->tet == (tetrahedron *) NULL) return true;
  else return t->tet[4] == (tetrahedron) NULL;
}

inline bool tetgenmesh::isdead(face* s) {
  if (s->sh == (shellface *) NULL) return true;
  else return s->sh[3] == (shellface) NULL;
}

// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices
//   of the tetface 't' subface 's'.

inline bool tetgenmesh::isfacehaspoint(triface* t, point testpoint) {
  return ((org(*t) == testpoint) || (dest(*t) == testpoint) ||
          (apex(*t) == testpoint));
}

inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) {
  return (s->sh[3] == (shellface) testpoint) || 
         (s->sh[4] == (shellface) testpoint) ||
         (s->sh[5] == (shellface) testpoint);
}

// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is
//   one of the three edges of the subface 's'.

inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) {
  return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2));
}

// issymexist() returns TRUE if the adjoining tetrahedron is not 'duumytet'.

inline bool tetgenmesh::issymexist(triface* t) {
  tetrahedron *ptr = (tetrahedron *) 
    ((size_t)(t->tet[t->loc]) & ~(size_t)7l);
  return ptr != dummytet;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getnextsface()    Finds the next subface in the face ring.                //
//                                                                           //
// For saving space in the data structure of subface, there only exists one  //
// face ring around a segment (see programming manual).  This routine imple- //
// ments the double face ring as desired in Muecke's data structure.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getnextsface(face* s1, face* s2)
{
  face neighsh, spinsh;
  face testseg;

  sspivot(*s1, testseg);
  if (testseg.sh != dummysh) {
    testseg.shver = 0;
    if (sorg(testseg) == sorg(*s1)) {
      spivot(*s1, neighsh);
    } else {
      spinsh = *s1;
      do {
        neighsh = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != s1->sh);
    }
  } else {
    spivot(*s1, neighsh);
  }
  if (sorg(neighsh) != sorg(*s1)) {
    sesymself(neighsh);
  }
  if (s2 != (face *) NULL) {
    *s2 = neighsh;
  } else {
    *s1 = neighsh;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tsspivot()    Finds a subsegment abutting on a tetrahderon's edge.        //
//                                                                           //
// The edge is represented in the primary edge of 'checkedge'. If there is a //
// subsegment bonded at this edge, it is returned in handle 'checkseg', the  //
// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, //
// set 'checkseg.sh = dummysh' to indicate it is not a subsegment.           //
//                                                                           //
// To find whether an edge of a tetrahedron is a subsegment or not. First we //
// need find a subface around this edge to see if it contains a subsegment.  //
// The reason is there is no direct connection between a tetrahedron and its //
// adjoining subsegments.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tsspivot(triface* checkedge, face* checkseg)
{
  triface spintet;
  face parentsh;
  point tapex;
  int hitbdry;

  spintet = *checkedge;
  tapex = apex(*checkedge);
  hitbdry = 0;
  do {
    tspivot(spintet, parentsh);
    // Does spintet have a (non-fake) subface attached? 
    if ((parentsh.sh != dummysh) && (sapex(parentsh) != NULL)) {
      // Find a subface! Find the edge in it.      
      findedge(&parentsh, org(*checkedge), dest(*checkedge));
      sspivot(parentsh, *checkseg);
      if (checkseg->sh != dummysh) {
        // Find a subsegment! Correct its edge direction before return.
        if (sorg(*checkseg) != org(*checkedge)) {
          sesymself(*checkseg);
        }
      }
      return;
    }
    if (!fnextself(spintet)) {
      hitbdry++;
      if (hitbdry < 2) {
        esym(*checkedge, spintet);
        if (!fnextself(spintet)) {
          hitbdry++;
        }
      }
    }
  } while ((apex(spintet) != tapex) && (hitbdry < 2));
  // Not find.
  checkseg->sh = dummysh;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sstpivot()    Finds a tetrahedron abutting a subsegment.                  //
//                                                                           //
// This is the inverse operation of 'tsspivot()'.  One subsegment shared by  //
// arbitrary number of tetrahedron, the returned tetrahedron is not unique.  //
// The edge direction of the returned tetrahedron is conformed to the given  //
// subsegment.                                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::sstpivot(face* checkseg, triface* retedge)
{
  face parentsh;

  // Get the subface which holds the subsegment.
  sdecode(checkseg->sh[0], parentsh);
#ifdef SELF_CHECK
  assert(parentsh.sh != dummysh);
#endif
  // Get a tetraheron to which the subface attches.
  stpivot(parentsh, *retedge);
  if (retedge->tet == dummytet) {
    sesymself(parentsh);
    stpivot(parentsh, *retedge);
#ifdef SELF_CHECK
    assert(retedge->tet != dummytet);
#endif
  }
  // Correct the edge direction before return.
  findedge(retedge, sorg(*checkseg), sdest(*checkseg));
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findorg()    Finds a point in the given handle (tetrahedron or subface).  //
//                                                                           //
// If 'dorg' is a one of vertices of the given handle,  set the origin of    //
// this handle be that point and return TRUE.  Otherwise, return FALSE and   //
// 'tface' remains unchanged.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findorg(triface* tface, point dorg)
{
  if (org(*tface) == dorg) {
    return true;
  } else { 
    if (dest(*tface) == dorg) {
      enextself(*tface);
      return true;
    } else {
      if (apex(*tface) == dorg) {
        enext2self(*tface);
        return true;
      } else {
        if (oppo(*tface) == dorg) {
          // Keep 'tface' referring to the same tet after fnext().
          adjustedgering(*tface, CCW);
          fnextself(*tface);
          enext2self(*tface);
          return true;
        } 
      }
    }
  }
  return false;
}

bool tetgenmesh::findorg(face* sface, point dorg)
{
  if (sorg(*sface) == dorg) {
    return true;
  } else {
    if (sdest(*sface) == dorg) {
      senextself(*sface);
      return true;
    } else {
      if (sapex(*sface) == dorg) {
        senext2self(*sface);
        return true;
      } 
    }
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findedge()    Find an edge in the given handle (tetrahedron or subface).  //
//                                                                           //
// The edge is given in two points 'eorg' and 'edest'.  It is assumed that   //
// the edge must exist in the given handle (tetrahedron or subface).  This   //
// routine sets the right edge version for the input handle.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::findedge(triface* tface, point eorg, point edest)
{
  int i;

  for (i = 0; i < 3; i++) {
    if (org(*tface) == eorg) {
      if (dest(*tface) == edest) {
        // Edge is found, return.
        return;
      } 
    } else {
      if (org(*tface) == edest) {
        if (dest(*tface) == eorg) {
          // Edge is found, inverse the direction and return.
          esymself(*tface);
          return;
        }
      }
    }
    enextself(*tface);
  }
  // It should never be here.
  printf("Internalerror in findedge():  Unable to find an edge in tet.\n");
  internalerror();
}

void tetgenmesh::findedge(face* sface, point eorg, point edest)
{
  int i;

  for (i = 0; i < 3; i++) {
    if (sorg(*sface) == eorg) {
      if (sdest(*sface) == edest) {
        // Edge is found, return.
        return;
      } 
    } else {
      if (sorg(*sface) == edest) {
        if (sdest(*sface) == eorg) {
          // Edge is found, inverse the direction and return.
          sesymself(*sface);
          return;
        }
      }
    }
    senextself(*sface);
  }
  printf("Internalerror in findedge():  Unable to find an edge in subface.\n");
  internalerror();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findface()    Find the face has the given origin, destination and apex.   //
//                                                                           //
// On input, 'fface' is a handle which may contain the three corners or may  //
// not or may be dead.  On return, it represents exactly the face with the   //
// given origin, destination and apex.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::findface(triface *fface, point forg, point fdest, point fapex)
{
  triface spintet;
  enum finddirectionresult collinear;
  int hitbdry;

  if (!isdead(fface)) {
    // First check the easiest case, that 'fface' is just the right one.
    if (org(*fface) == forg && dest(*fface) == fdest && 
        apex(*fface) == fapex) return;
  } else {
    // The input handle is dead, use the 'recenttet' if it is alive.
    if (!isdead(&recenttet)) *fface = recenttet;
  }

  if (!isdead(fface)) {
    if (!findorg(fface, forg)) {
      // 'forg' is not a corner of 'fface', locate it.
      preciselocate(forg, fface, tetrahedrons->items);
    }
    // It is possible that forg is not found in a non-convex mesh.
    if (org(*fface) == forg) {
      collinear = finddirection(fface, fdest, tetrahedrons->items);
      if (collinear == RIGHTCOLLINEAR) {
        // fdest is just the destination.
      } else if (collinear == LEFTCOLLINEAR) {
        enext2self(*fface);
        esymself(*fface);
      } else if (collinear == TOPCOLLINEAR) {
        fnextself(*fface);
        enext2self(*fface);
        esymself(*fface);
      }
    }
    // It is possible taht fdest is not found in a non-convex mesh.
    if ((org(*fface) == forg) && (dest(*fface) == fdest)) {
      // Find the apex of 'fapex'.
      spintet = *fface;
      hitbdry = 0;
      do {
        if (apex(spintet) == fapex) {
          // We have done. Be careful the edge direction of 'spintet',
          //   it may reversed because of hitting boundary once.
          if (org(spintet) != org(*fface)) {
            esymself(spintet);
          }
          *fface = spintet;
          return;
        }
        if (!fnextself(spintet)) {
          hitbdry ++;
          if (hitbdry < 2) {
            esym(*fface, spintet);
            if (!fnextself(spintet)) {
              hitbdry ++;
            }
          }
        }
      } while (hitbdry < 2 && apex(spintet) != apex(*fface));
      // It is possible that fapex is not found in a non-convex mesh.
    }
  }

  if (isdead(fface) || (org(*fface) != forg) || (dest(*fface) != fdest) ||
      (apex(*fface) != fapex)) {
    // Too bad, the input handle is useless. We have to find a handle
    //   for 'fface' contains the 'forg' and 'fdest'. Here a brute force
    //   search is performed.
    if (b->verbose > 1) {
      printf("Warning in findface():  Perform a brute-force searching.\n");
    }
    enum verttype forgty, fdestty, fapexty;
    int share, i;
    forgty = pointtype(forg);
    fdestty = pointtype(fdest);
    fapexty = pointtype(fapex);
    setpointtype(forg, DEADVERTEX);
    setpointtype(fdest, DEADVERTEX);
    setpointtype(fapex, DEADVERTEX);
    tetrahedrons->traversalinit();
    fface->tet = tetrahedrontraverse();
    while (fface->tet != (tetrahedron *) NULL) {
      share = 0;
      for (i = 0; i < 4; i++) {
        if (pointtype((point) fface->tet[4 + i]) == DEADVERTEX) share ++;
      }
      if (share == 3) {
        // Found! Set the correct face and desired corners.
        if (pointtype((point) fface->tet[4]) != DEADVERTEX) {
          fface->loc = 2;
        } else if (pointtype((point) fface->tet[5]) != DEADVERTEX) {
          fface->loc = 3;
        } else if (pointtype((point) fface->tet[6]) != DEADVERTEX) {
          fface->loc = 1;
        } else { // pointtype((point) fface->tet[7]) != DEADVERTEX
          fface->loc = 0;
        }
        findedge(fface, forg, fdest);
        break;
      }
      fface->tet = tetrahedrontraverse();
    }
    setpointtype(forg, forgty);
    setpointtype(fdest, fdestty);
    setpointtype(fapex, fapexty);
    if (fface->tet == (tetrahedron *) NULL) {
      // It is impossible to reach here.
      printf("Internal error:  Fail to find the indicated face.\n");
      internalerror();
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getonextseg()    Get the next SEGMENT counterclockwise with the same org. //
//                                                                           //
// 's' is a subface. This routine reteuns the segment which is counterclock- //
// wise with the origin of s.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getonextseg(face* s, face* lseg)
{
  face checksh, checkseg;
  point forg;

  forg = sorg(*s);
  checksh = *s;
  do {
    // Go to the edge at forg's left side.
    senext2self(checksh);
    // Check if there is a segment attaching this edge.
    sspivot(checksh, checkseg);
    if (checkseg.sh != dummysh) break;
    // No segment! Go to the neighbor of this subface.
    spivotself(checksh);
#ifdef SELF_CHECK
    // It should always meet a segment before come back.
    assert(checksh.sh != s->sh);
#endif
    if (sorg(checksh) != forg) {
      sesymself(checksh);
#ifdef SELF_CHECK
      assert(sorg(checksh) == forg);
#endif
    }
  } while (true);
  if (sorg(checkseg) != forg) sesymself(checkseg);
  *lseg = checkseg;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getseghasorg()    Get the segment containing the given point.             //
//                                                                           //
// 'dorg' is an endpoint of a segment S. 'sseg' is a subsegment of S. This   //
// routine search a subsegment (along sseg) of S containing dorg. On return, //
// 'sseg' contains 'dorg' as its origin.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getseghasorg(face* sseg, point dorg)
{
  face nextseg;
  point checkpt;

  nextseg = *sseg;
  checkpt = sorg(nextseg);
  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
    // Search dorg along the original direction of sseg.
    senext2self(nextseg);
    spivotself(nextseg);
    nextseg.shver = 0;
    if (sdest(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sorg(nextseg);
  }
  if (checkpt == dorg) {
    *sseg = nextseg;
    return;
  }
  nextseg = *sseg;
  checkpt = sdest(nextseg);
  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
    // Search dorg along the destinational direction of sseg.
    senextself(nextseg);
    spivotself(nextseg);
    nextseg.shver = 0;
    if (sorg(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sdest(nextseg);
  }
  if (checkpt == dorg) {
    sesym(nextseg, *sseg);
    return;
  }
  // Should never be here.
  printf("Internalerror in getseghasorg():  Unable to find the subseg.\n");
  internalerror();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubsegfarorg()    Get the origin of the parent segment of a subseg.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg)
{
  face prevseg;
  point checkpt;

  checkpt = sorg(*sseg);
  senext2(*sseg, prevseg);
  spivotself(prevseg);
  // Search dorg along the original direction of sseg.
  while (prevseg.sh != dummysh) {
    prevseg.shver = 0;
    if (sdest(prevseg) != checkpt) sesymself(prevseg);
    checkpt = sorg(prevseg);
    senext2self(prevseg);
    spivotself(prevseg);
  }
  return checkpt;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubsegfardest()    Get the dest. of the parent segment of a subseg.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg)
{
  face nextseg;
  point checkpt;

  checkpt = sdest(*sseg);
  senext(*sseg, nextseg);
  spivotself(nextseg);
  // Search dorg along the destinational direction of sseg.
  while (nextseg.sh != dummysh) {
    nextseg.shver = 0;
    if (sorg(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sdest(nextseg);
    senextself(nextseg);
    spivotself(nextseg);
  }
  return checkpt;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// printtet()    Print out the details of a tetrahedron on screen.           //
//                                                                           //
// It's also used when the highest level of verbosity (`-VVV') is specified. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::printtet(triface* tface)
{
  triface tmpface, prtface;
  point tmppt;
  face tmpsh;
  int facecount;

  printf("Tetra x%lx with loc(%i) and ver(%i):",
         (unsigned long)(tface->tet), tface->loc, tface->ver);
  if (infected(*tface)) {
    printf(" (infected)");
  }
  printf("\n");

  tmpface = *tface;
  facecount = 0;
  while(facecount < 4) {
    tmpface.loc = facecount;
    sym(tmpface, prtface);
    if(prtface.tet == dummytet) {
      printf("      [%i] Outer space.\n", facecount);
    } else {
      printf("      [%i] x%lx  loc(%i).", facecount,
             (unsigned long)(prtface.tet), prtface.loc);
      if (infected(prtface)) {
        printf(" (infected)");
      }
      printf("\n");
    }
    facecount ++;
  }

  tmppt = org(*tface);
  if(tmppt == (point) NULL) {
    printf("      Org [%i] NULL\n", locver2org[tface->loc][tface->ver]);
  } else {
    printf("      Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           locver2org[tface->loc][tface->ver], (unsigned long)(tmppt),
           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = dest(*tface);
  if(tmppt == (point) NULL) {
    printf("      Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]);
  } else {
    printf("      Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           locver2dest[tface->loc][tface->ver], (unsigned long)(tmppt),
           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = apex(*tface);
  if(tmppt == (point) NULL) {
    printf("      Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]);
  } else {
    printf("      Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           locver2apex[tface->loc][tface->ver], (unsigned long)(tmppt),
           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = oppo(*tface);
  if(tmppt == (point) NULL) {
    printf("      Oppo[%i] NULL\n", loc2oppo[tface->loc]);
  } else {
    printf("      Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           loc2oppo[tface->loc], (unsigned long)(tmppt),
           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }

  if (b->useshelles) {
    tmpface = *tface;
    facecount = 0;
    while(facecount < 6) {
      tmpface.loc = facecount;
      tspivot(tmpface, tmpsh);
      if(tmpsh.sh != dummysh) {
        printf("      [%i] x%lx  ID(%i) ", facecount,
               (unsigned long)(tmpsh.sh), shellmark(tmpsh));
        if (sorg(tmpsh) == (point) NULL) {
          printf("(fake)");
        }
        printf("\n");
      }
      facecount ++;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// printsh()    Print out the details of a subface or subsegment on screen.  //
//                                                                           //
// It's also used when the highest level of verbosity (`-VVV') is specified. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::printsh(face* sface)
{
  face prtsh;
  triface prttet;
  point printpoint;

  if (sapex(*sface) != NULL) {
    printf("subface x%lx, ver %d, mark %d:",
           (unsigned long)(sface->sh), sface->shver, shellmark(*sface));
  } else {
    printf("Subsegment x%lx, ver %d, mark %d:",
           (unsigned long)(sface->sh), sface->shver, shellmark(*sface));
  }
  if (sinfected(*sface)) {
    printf(" (infected)");
  }
  if (shell2badface(*sface)) {
    printf(" (queued)");
  }
  if (sapex(*sface) != NULL) {
    if (shelltype(*sface) == SHARP) {
      printf(" (sharp)");
    }
  } else {
    if (shelltype(*sface) == SHARP) {
      printf(" (sharp)");
    }
  }
  if (checkpbcs) {
    if (shellpbcgroup(*sface) >= 0) {
      printf(" (pbc %d)", shellpbcgroup(*sface));
    }
  }
  printf("\n");

  sdecode(sface->sh[0], prtsh);
  if (prtsh.sh == dummysh) {
    printf("      [0] = No shell\n");
  } else {
    printf("      [0] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
  }
  sdecode(sface->sh[1], prtsh);
  if (prtsh.sh == dummysh) {
    printf("      [1] = No shell\n");
  } else {
    printf("      [1] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
  }
  sdecode(sface->sh[2], prtsh);
  if (prtsh.sh == dummysh) {
    printf("      [2] = No shell\n");
  } else {
    printf("      [2] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
  }

  printpoint = sorg(*sface);
  if (printpoint == (point) NULL)
    printf("      Org [%d] = NULL\n", vo[sface->shver]);
  else
    printf("      Org [%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
           vo[sface->shver], (unsigned long)(printpoint), printpoint[0],
           printpoint[1], printpoint[2], pointmark(printpoint));
  printpoint = sdest(*sface);
  if (printpoint == (point) NULL)
    printf("      Dest[%d] = NULL\n", vd[sface->shver]);
  else
    printf("      Dest[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
            vd[sface->shver], (unsigned long)(printpoint), printpoint[0],
            printpoint[1], printpoint[2], pointmark(printpoint));

  if (sapex(*sface) != NULL) {
    printpoint = sapex(*sface);
    if (printpoint == (point) NULL)
      printf("      Apex[%d] = NULL\n", va[sface->shver]);
    else
      printf("      Apex[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
             va[sface->shver], (unsigned long)(printpoint), printpoint[0],
             printpoint[1], printpoint[2], pointmark(printpoint));

    decode(sface->sh[6], prttet);
    if (prttet.tet == dummytet) {
      printf("      [6] = Outer space\n");
    } else {
      printf("      [6] = x%lx  %d\n",
             (unsigned long)(prttet.tet), prttet.loc);
    }
    decode(sface->sh[7], prttet);
    if (prttet.tet == dummytet) {
      printf("      [7] = Outer space\n");
    } else {
      printf("      [7] = x%lx  %d\n",
             (unsigned long)(prttet.tet), prttet.loc);
    }

    sdecode(sface->sh[8], prtsh);
    if (prtsh.sh == dummysh) {
      printf("      [8] = No subsegment\n");
    } else {
      printf("      [8] = x%lx  %d\n",
             (unsigned long)(prtsh.sh), prtsh.shver);
    }
    sdecode(sface->sh[9], prtsh);
    if (prtsh.sh == dummysh) {
      printf("      [9] = No subsegment\n");
    } else {
      printf("      [9] = x%lx  %d\n",
             (unsigned long)(prtsh.sh), prtsh.shver);
    }
    sdecode(sface->sh[10], prtsh);
    if (prtsh.sh == dummysh) {
      printf("      [10]= No subsegment\n");
    } else {
      printf("      [10]= x%lx  %d\n",
             (unsigned long)(prtsh.sh), prtsh.shver);
    }
  } 
}

//
// End of advanced primitives
//

//
// End of mesh manipulation primitives
//

//
// Begin of mesh items searching routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makepoint2tetmap()    Construct a mapping from points to tetrahedra.      //
//                                                                           //
// Traverses all the tetrahedra,  provides each corner of each tetrahedron   //
// with a pointer to that tetrahedera.  Some pointers will be overwritten by //
// other pointers because each point may be a corner of several tetrahedra,  //
// but in the end every point will point to a tetrahedron that contains it.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makepoint2tetmap()
{
  triface tetloop;
  point pointptr;

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

  // Initialize the point2tet field of each point.
  points->traversalinit();
  pointptr = pointtraverse();
  while (pointptr != (point) NULL) {
    setpoint2tet(pointptr, (tetrahedron) NULL);
    pointptr = pointtraverse();
  }

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Check all four points of the tetrahedron.
    tetloop.loc = 0;
    pointptr = org(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = dest(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = apex(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = oppo(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    // Get the next tetrahedron in the list.
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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[0]'.  Don't forget to minus 'in->firstnumber' when  //
// to get the vertex form its index.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

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

  idx2verlist = new point[points->items];

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

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makesegmentmap()    Create a map from vertices (their indices) to         //
//                     segments incident at the same vertices.               //
//                                                                           //
// Two arrays 'idx2seglist' and 'segsperverlist' together return the map.    //
// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
// number of segments.  idx2seglist contains row information and             //
// segsperverlist contains all (non-zero) elements.  The i-th entry of       //
// idx2seglist is the starting position of i-th row's (non-zero) elements in //
// segsperverlist.  The number of elements of i-th row is calculated by the  //
// (i+1)-th entry minus i-th entry of idx2seglist.                           //
//                                                                           //
// NOTE: These two arrays will be created inside this routine, don't forget  //
// to free them after using.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist)
{
  shellface *shloop;
  int i, j, k;

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

  // Create and initialize 'idx2seglist'.
  idx2seglist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0;

  // Loop the set of segments once, counter the number of segments sharing
  //   each vertex.
  subsegs->traversalinit();
  shloop = shellfacetraverse(subsegs);
  while (shloop != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 2; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      idx2seglist[j]++;
    }
    shloop = shellfacetraverse(subsegs);
  }

  // Calculate the total length of array 'facesperverlist'.
  j = idx2seglist[0];
  idx2seglist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2seglist[i + 1];
    idx2seglist[i + 1] = idx2seglist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2seglist.
  segsperverlist = new shellface*[idx2seglist[i]];
  // Loop the set of segments again, set the info. of segments per vertex.
  subsegs->traversalinit();
  shloop = shellfacetraverse(subsegs);
  while (shloop != (shellface *) NULL) {
    for (i = 0; i < 2; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      segsperverlist[idx2seglist[j]] = shloop;
      idx2seglist[j]++;
    }
    shloop = shellfacetraverse(subsegs);
  }
  // Contents in 'idx2seglist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2seglist[i + 1] = idx2seglist[i];
  }
  idx2seglist[0] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makesubfacemap()    Create a map from vertices (their indices) to         //
//                     subfaces incident at the same vertices.               //
//                                                                           //
// Two arrays 'idx2facelist' and 'facesperverlist' together return the map.  //
// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
// number of subfaces.  idx2facelist contains row information and            //
// facesperverlist contains all (non-zero) elements.  The i-th entry of      //
// idx2facelist is the starting position of i-th row's(non-zero) elements in //
// facesperverlist.  The number of elements of i-th row is calculated by the //
// (i+1)-th entry minus i-th entry of idx2facelist.                          //
//                                                                           //
// NOTE: These two arrays will be created inside this routine, don't forget  //
// to free them after using.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
makesubfacemap(int*& idx2facelist, shellface**& facesperverlist)
{
  shellface *shloop;
  int i, j, k;

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

  // Create and initialize 'idx2facelist'.
  idx2facelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2facelist[i] = 0;

  // Loop the set of subfaces once, counter the number of subfaces sharing
  //   each vertex.
  subfaces->traversalinit();
  shloop = shellfacetraverse(subfaces);
  while (shloop != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 3; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      idx2facelist[j]++;
    }
    shloop = shellfacetraverse(subfaces);
  }

  // Calculate the total length of array 'facesperverlist'.
  j = idx2facelist[0];
  idx2facelist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2facelist[i + 1];
    idx2facelist[i + 1] = idx2facelist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2facelist.
  facesperverlist = new shellface*[idx2facelist[i]];
  // Loop the set of segments again, set the info. of segments per vertex.
  subfaces->traversalinit();
  shloop = shellfacetraverse(subfaces);
  while (shloop != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      facesperverlist[idx2facelist[j]] = shloop;
      idx2facelist[j]++;
    }
    shloop = shellfacetraverse(subfaces);
  }
  // Contents in 'idx2facelist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2facelist[i + 1] = idx2facelist[i];
  }
  idx2facelist[0] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// maketetrahedronmap()    Create a map from vertices (their indices) to     //
//                         tetrahedra incident at the same vertices.         //
//                                                                           //
// Two arrays 'idx2tetlist' and 'tetsperverlist' together return the map.    //
// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
// number of tetrahedra.  idx2tetlist contains row information and           //
// tetsperverlist contains all (non-zero) elements.  The i-th entry of       //
// idx2tetlist is the starting position of i-th row's (non-zero) elements in //
// tetsperverlist.  The number of elements of i-th row is calculated by the  //
// (i+1)-th entry minus i-th entry of idx2tetlist.                           //
//                                                                           //
// NOTE: These two arrays will be created inside this routine, don't forget  //
// to free them after using.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist)
{
  tetrahedron *tetloop;
  int i, j, k;

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

  // Create and initialize 'idx2tetlist'.
  idx2tetlist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0;

  // Loop the set of tetrahedra once, counter the number of tetrahedra
  //   sharing each vertex.
  tetrahedrons->traversalinit();
  tetloop = tetrahedrontraverse();
  while (tetloop != (tetrahedron *) NULL) {
    // Increment the number of sharing tetrahedra for each endpoint.
    for (i = 0; i < 4; i++) {
      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
      idx2tetlist[j]++;
    }
    tetloop = tetrahedrontraverse();
  }

  // Calculate the total length of array 'tetsperverlist'.
  j = idx2tetlist[0];
  idx2tetlist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2tetlist[i + 1];
    idx2tetlist[i + 1] = idx2tetlist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2tetlist.
  tetsperverlist = new tetrahedron*[idx2tetlist[i]];
  // Loop the set of tetrahedra again, set the info. of tet. per vertex.
  tetrahedrons->traversalinit();
  tetloop = tetrahedrontraverse();
  while (tetloop != (tetrahedron *) NULL) {
    for (i = 0; i < 4; i++) {
      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
      tetsperverlist[idx2tetlist[j]] = tetloop;
      idx2tetlist[j]++;
    }
    tetloop = tetrahedrontraverse();
  }
  // Contents in 'idx2tetlist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2tetlist[i + 1] = idx2tetlist[i];
  }
  idx2tetlist[0] = 0;
}

//
// End of mesh items searching routines
//

//
// Begin of linear algebra functions
//

// dot() returns the dot product: v1 dot v2.

inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) 
{
  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}

// cross() computes the cross product: n = v1 cross v2.

inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) 
{
  n[0] =   v1[1] * v2[2] - v2[1] * v1[2];
  n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]);
  n[2] =   v1[0] * v2[1] - v2[0] * v1[1];
}

// initm44() initializes a 4x4 matrix.
static void initm44(REAL a00, REAL a01, REAL a02, REAL a03,
                    REAL a10, REAL a11, REAL a12, REAL a13,
                    REAL a20, REAL a21, REAL a22, REAL a23,
                    REAL a30, REAL a31, REAL a32, REAL a33,
                    REAL M[4][4])
{
  M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03;
  M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13;
  M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23;
  M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33;
}

// m4xm4() multiplies 2 4x4 matrics:  m1 = m1 * m2.
static void m4xm4(REAL m1[4][4], REAL m2[4][4])
{
  REAL tmp[4];
  int i, j;

  for (i = 0; i < 4; i++) {   // i-th row
    for (j = 0; j < 4; j++) { // j-th col
      tmp[j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] 
             + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j];
    }
    for (j = 0; j < 4; j++) 
      m1[i][j] = tmp[j];
  }
}

// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1
static void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4])
{
  v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3];
  v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3];
  v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3];
  v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3];
}

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

//
// End of linear algebra functions
//

//
// Begin of geometric tests
//

// All the following routines require the input objects are not degenerate.
//   i.e., a triangle must has three non-collinear corners; an edge must
//   has two identical endpoints.  Degenerate cases should have to detect
//   first and then handled as special cases.

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edge_vert_col_inter()    Test whether an edge (ab) and a collinear vertex //
//                          (p) are intersecting or not.                     //
//                                                                           //
// Possible cases are p is coincident to a (p = a), or to b (p = b), or p is //
// inside ab (a < p < b), or outside ab (p < a or p > b). These cases can be //
// quickly determined by comparing the corresponding coords of a, b, and p   //
// (which are not all equal).                                                //
//                                                                           //
// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX  //
// (p = a or p = b), and INTERSECT (a < p < b).                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B,
  REAL* P)
{
  int i = 0;
  do {
    if (A[i] < B[i]) {
      if (P[i] < A[i]) {
        return DISJOINT;
      } else if (P[i] > A[i]) {
        if (P[i] < B[i]) {
          return INTERSECT;
        } else if (P[i] > B[i]) {
          return DISJOINT;
        } else {
          // assert(P[i] == B[i]);
          return SHAREVERTEX;
        }
      } else {
        // assert(P[i] == A[i]);
        return SHAREVERTEX;
      }
    } else if (A[i] > B[i]) {
      if (P[i] < B[i]) {
        return DISJOINT;
      } else if (P[i] > B[i]) {
        if (P[i] < A[i]) {
          return INTERSECT;
        } else if (P[i] > A[i]) {
          return DISJOINT;
        } else {
          // assert(P[i] == A[i]);
          return SHAREVERTEX;
        }
      } else {
        // assert(P[i] == B[i]);
        return SHAREVERTEX;
      }
    }
    // i-th coordinates are equal, try i+1-th;
    i++;
  } while (i < 3);
  // Should never be here.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edge_edge_cop_inter()    Test whether two coplanar edges (ab, and pq) are //
//                          intersecting or not.                             //
//                                                                           //
// Possible cases are ab and pq are disjointed, or proper intersecting (int- //
// ersect at a point other than their endpoints), or both collinear and int- //
// ersecting, or sharing at a common endpoint, or are coincident.            //
//                                                                           //
// A reference point R is required, which is exactly not coplanar with these //
// two edges.  Since the caller knows these two edges are coplanar, it must  //
// be able to provide (or calculate) such a point.                           //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B,
  REAL* P, REAL* Q, REAL* R)
{
  REAL s1, s2, s3, s4;

#ifdef SELF_CHECK
  assert(R != NULL);
#endif
  s1 = orient3d(A, B, R, P);
  s2 = orient3d(A, B, R, Q);
  if (s1 * s2 > 0.0) {
    // Both p and q are at the same side of ab.
    return DISJOINT;
  }
  s3 = orient3d(P, Q, R, A);
  s4 = orient3d(P, Q, R, B);
  if (s3 * s4 > 0.0) {
    // Both a and b are at the same side of pq.
    return DISJOINT;
  }

  // Possible degenerate cases are:
  //   (1) Only one of p and q is collinear with ab;
  //   (2) Both p and q are collinear with ab;
  //   (3) Only one of a and b is collinear with pq. 
  enum interresult abp, abq;
  enum interresult pqa, pqb;

  if (s1 == 0.0) {
    // p is collinear with ab.
    abp = edge_vert_col_inter(A, B, P);
    if (abp == INTERSECT) {
      // p is inside ab.
      return INTERSECT;
    }
    if (s2 == 0.0) {
      // q is collinear with ab. Case (2).
      abq = edge_vert_col_inter(A, B, Q);
      if (abq == INTERSECT) {
        // q is inside ab.
        return INTERSECT;
      }
      if (abp == SHAREVERTEX && abq == SHAREVERTEX) {
        // ab and pq are identical.
        return SHAREEDGE;
      }
      pqa = edge_vert_col_inter(P, Q, A);
      if (pqa == INTERSECT) {
        // a is inside pq.
        return INTERSECT;
      }
      pqb = edge_vert_col_inter(P, Q, B);
      if (pqb == INTERSECT) {
        // b is inside pq.
        return INTERSECT;
      }
      if (abp == SHAREVERTEX || abq == SHAREVERTEX) {
        // either p or q is coincident with a or b.
#ifdef SELF_CHECK
        // ONLY one case is possible, otherwise, shoule be SHAREEDGE.
        assert(abp ^ abq);
#endif
        return SHAREVERTEX;
      }
      // The last case. They are disjointed.
#ifdef SELF_CHECK
      assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb));
#endif
      return DISJOINT;
    } else {
      // p is collinear with ab. Case (1).
#ifdef SELF_CHECK
      assert(abp == SHAREVERTEX || abp == DISJOINT);
#endif
      return abp;
    }
  }
  // p is NOT collinear with ab.
  if (s2 == 0.0) {
    // q is collinear with ab. Case (1).
    abq = edge_vert_col_inter(A, B, Q);
#ifdef SELF_CHECK
    assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT);
#endif
    return abq;
  }

  // We have found p and q are not collinear with ab. However, it is still
  //   possible that a or b is collinear with pq (ONLY one of a and b).
  if (s3 == 0.0) {
    // a is collinear with pq. Case (3).
#ifdef SELF_CHECK
    assert(s4 != 0.0);
#endif
    pqa = edge_vert_col_inter(P, Q, A);
#ifdef SELF_CHECK
    // This case should have been detected in above.
    assert(pqa != SHAREVERTEX);
    assert(pqa == INTERSECT || pqa == DISJOINT);
#endif
    return pqa;
  }
  if (s4 == 0.0) {
    // b is collinear with pq. Case (3).
#ifdef SELF_CHECK
    assert(s3 != 0.0);
#endif
    pqb = edge_vert_col_inter(P, Q, B);
#ifdef SELF_CHECK
    // This case should have been detected in above.
    assert(pqb != SHAREVERTEX);
    assert(pqb == INTERSECT || pqb == DISJOINT);
#endif
    return pqb;
  }

  // ab and pq are intersecting properly.
  return INTERSECT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Notations                                                                 //
//                                                                           //
// Let ABC be the plane passes through a, b, and c;  ABC+ be the halfspace   //
// including the set of all points x, such that orient3d(a, b, c, x) > 0;    //
// ABC- be the other halfspace, such that for each point x in ABC-,          //
// orient3d(a, b, c, x) < 0.  For the set of x which are on ABC, orient3d(a, //
// b, c, x) = 0.                                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_vert_copl_inter()    Test whether a triangle (abc) and a coplanar     //
//                          point (p) are intersecting or not.               //
//                                                                           //
// Possible cases are p is inside abc, or on an edge of, or coincident with  //
// a vertex of, or outside abc.                                              //
//                                                                           //
// A reference point R is required. R is exactly not coplanar with abc and p.//
// Since the caller knows they are coplanar, it must be able to provide (or  //
// calculate) such a point.                                                  //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// and INTERSECT.                                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* R)
{
  REAL s1, s2, s3;
  int sign;

#ifdef SELF_CHECK
  assert(R != (REAL *) NULL);
#endif
  // Adjust the orientation of a, b, c and r, so that we can assume that
  //   r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule).
  s1 = orient3d(A, B, C, R);
#ifdef SELF_CHECK
  assert(s1 != 0.0);
#endif
  sign = s1 < 0.0 ? 1 : -1;

  // Test starts from here.
  s1 = orient3d(A, B, R, P) * sign;
  if (s1 < 0.0) {
    // p is in ABR-.
    return DISJOINT;
  }
  s2 = orient3d(B, C, R, P) * sign;
  if (s2 < 0.0) {
    // p is in BCR-.
    return DISJOINT;
  }
  s3 = orient3d(C, A, R, P) * sign;
  if (s3 < 0.0) {
    // p is in CAR-.
    return DISJOINT;
  }
  if (s1 == 0.0) {
    // p is on ABR.
    if (s2 == 0.0) {
      // p is on BCR.
#ifdef SELF_CHECK
      assert(s3 > 0.0);
#endif
      // p is coincident with b.
      return SHAREVERTEX;
    }
    if (s3 == 0.0) {
      // p is on CAR.
      // p is coincident with a.
      return SHAREVERTEX;
    }
    // p is on edge ab.
    return INTERSECT;
  }
  // p is in ABR+.
  if (s2 == 0.0) {
    // p is on BCR.
    if (s3 == 0.0) {
      // p is on CAR.
      // p is coincident with c.
      return SHAREVERTEX;
    }
    // p is on edge bc.
    return INTERSECT;
  }
  if (s3 == 0.0) {
    // p is on CAR.
    // p is on edge ca.
    return INTERSECT;
  }

  // p is strictly inside abc.
  return INTERSECT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_cop_inter()    Test whether a triangle (abc) and a coplanar edge //
//                         (pq) are intersecting or not.                     //
//                                                                           //
// A reference point R is required. R is exactly not coplanar with abc and   //
// pq.  Since the caller knows they are coplanar, it must be able to provide //
// (or calculate) such a point.                                              //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q, REAL* R)
{
  enum interresult abpq, bcpq, capq;
  enum interresult abcp, abcq;

  // Test if pq is intersecting one of edges of abc.
  abpq = edge_edge_cop_inter(A, B, P, Q, R);
  if (abpq == INTERSECT || abpq == SHAREEDGE) {
    return abpq;
  }
  bcpq = edge_edge_cop_inter(B, C, P, Q, R);
  if (bcpq == INTERSECT || bcpq == SHAREEDGE) {
    return bcpq;
  }
  capq = edge_edge_cop_inter(C, A, P, Q, R);
  if (capq == INTERSECT || capq == SHAREEDGE) {
    return capq;
  }
  
  // Test if p and q is inside abc.
  abcp = tri_vert_cop_inter(A, B, C, P, R);
  if (abcp == INTERSECT) {
    return INTERSECT;
  }
  abcq = tri_vert_cop_inter(A, B, C, Q, R);
  if (abcq == INTERSECT) {
    return INTERSECT;
  }

  // Combine the test results of edge intersectings and triangle insides
  //   to detect whether abc and pq are sharing vertex or disjointed.
  if (abpq == SHAREVERTEX) {
    // p or q is coincident with a or b.
#ifdef SELF_CHECK
    assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }
  if (bcpq == SHAREVERTEX) {
    // p or q is coincident with b or c.
#ifdef SELF_CHECK
    assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }
  if (capq == SHAREVERTEX) {
    // p or q is coincident with c or a.
#ifdef SELF_CHECK
    assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }

  // They are disjointed.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_inter_tail()    Test whether a triangle (abc) and an edge (pq)   //
//                          are intersecting or not.                         //
//                                                                           //
// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d(  //
// a, b, c, p); s2 = orient3d(a, b, c, q).  To separate this routine from    //
// tri_edge_inter() can save two orientation tests in tri_tri_inter().       //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2)
{
  REAL s3, s4, s5;
  int sign;

  if (s1 * s2 > 0.0) {
    // p, q are at the same halfspace of ABC, no intersection.
    return DISJOINT;
  }

  if (s1 * s2 < 0.0) {
    // p, q are both not on ABC (and not sharing vertices, edges of abc).
    // Adjust the orientation of a, b, c and p, so that we can assume that
    //   p is strictly in ABC-, and q is strictly in ABC+.
    sign = s1 < 0.0 ? 1 : -1;
    s3 = orient3d(A, B, P, Q) * sign;
    if (s3 < 0.0) {
      // q is at ABP-.
      return DISJOINT;
    }
    s4 = orient3d(B, C, P, Q) * sign;
    if (s4 < 0.0) {
      // q is at BCP-.
      return DISJOINT;
    }
    s5 = orient3d(C, A, P, Q) * sign;
    if (s5 < 0.0) {
      // q is at CAP-.
      return DISJOINT;
    }
    if (s3 == 0.0) {
      // q is on ABP.
      if (s4 == 0.0) {
        // q is on BCP (and q must in CAP+).
#ifdef SELF_CHECK
        assert(s5 > 0.0); 
#endif
        // pq intersects abc at vertex b.
        return SHAREVERTEX;
      }
      if (s5 == 0.0) {
        // q is on CAP (and q must in BCP+).
        // pq intersects abc at vertex a.
        return SHAREVERTEX;
      }
      // q in both BCP+ and CAP+.
      // pq crosses ab properly.
      return INTERSECT;
    }
    // q is in ABP+;
    if (s4 == 0.0) {
      // q is on BCP.
      if (s5 == 0.0) {
        // q is on CAP.
        // pq intersects abc at vertex c.
        return SHAREVERTEX;
      }
      // pq crosses bc properly.
      return INTERSECT;
    }
    // q is in BCP+;
    if (s5 == 0.0) {
      // q is on CAP.
      // pq crosses ca properly.
      return INTERSECT;
    }
    // q is in CAP+;
    // pq crosses abc properly.
    return INTERSECT;
  }

  if (s1 != 0.0 || s2 != 0.0) {
    // Either p or q is coplanar with abc. ONLY one of them is possible.
    if (s1 == 0.0) {
      // p is coplanar with abc, q can be used as reference point.
#ifdef SELF_CHECK
      assert(s2 != 0.0);
#endif
      return tri_vert_cop_inter(A, B, C, P, Q);
    } else {
      // q is coplanar with abc, p can be used as reference point.
#ifdef SELF_CHECK
      assert(s2 == 0.0);
#endif
      return tri_vert_cop_inter(A, B, C, Q, P);
    }
  }

  // pq is coplanar with abc.  Calculate a point which is exactly not
  //   coplanar with a, b, and c.
  REAL R[3], N[3];
  REAL ax, ay, az, bx, by, bz;
  
  ax = A[0] - B[0];
  ay = A[1] - B[1];
  az = A[2] - B[2];
  bx = A[0] - C[0];
  by = A[1] - C[1];
  bz = A[2] - C[2];
  N[0] = ay * bz - by * az;
  N[1] = az * bx - bz * ax;
  N[2] = ax * by - bx * ay;
  // The normal should not be a zero vector (otherwise, abc are collinear).
#ifdef SELF_CHECK
  assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0);
#endif
  // The reference point R is lifted from A to the normal direction with
  //   a distance d = average edge length of the triangle abc.
  R[0] = N[0] + A[0];
  R[1] = N[1] + A[1];
  R[2] = N[2] + A[2];
  // Becareful the case: if the non-zero component(s) in N is smaller than
  //   the machine epsilon (i.e., 2^(-16) for double), R will exactly equal
  //   to A due to the round-off error.  Do check if it is.
  if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) {
    int i, j;
    for (i = 0; i < 3; i++) {
#ifdef SELF_CHECK
      assert (R[i] == A[i]);
#endif
      j = 2;
      do {
        if (N[i] > 0.0) {
          N[i] += (j * macheps);
        } else {
          N[i] -= (j * macheps);
        }
        R[i] = N[i] + A[i];
        j *= 2;
      } while (R[i] == A[i]);
    }
  }

  return tri_edge_cop_inter(A, B, C, P, Q, R);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_inter()    Test whether a triangle (abc) and an edge (pq) are    //
//                     intersecting or not.                                  //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q)
{
  REAL s1, s2;

  // Test the locations of p and q with respect to ABC.
  s1 = orient3d(A, B, C, P);
  s2 = orient3d(A, B, C, Q);

  return tri_edge_inter_tail(A, B, C, P, Q, s1, s2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_tri_inter()    Test whether two triangle (abc) and (opq) are          //
//                    intersecting or not.                                   //
//                                                                           //
// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, SHAREFACE, and INTERSECT.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult 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 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 DISJOINT;
  }

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

  abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p);
  if (abcop == INTERSECT) {
    return INTERSECT;
  } else if (abcop == SHAREEDGE) {
    shareedge++;
  }
  abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
  if (abcpq == INTERSECT) {
    return INTERSECT;
  } else if (abcpq == SHAREEDGE) {
    shareedge++;
  }
  abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
  if (abcqo == INTERSECT) {
    return INTERSECT;
  } else if (abcqo == SHAREEDGE) {
    shareedge++;
  }
  if (shareedge == 3) {
    // opq are coincident with abc.
    return SHAREFACE;
  }
#ifdef SELF_CHECK
  // It is only possible either no share edge or one.
  assert(shareedge == 0 || shareedge == 1);
#endif

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

  opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
  if (opqab == INTERSECT) {
    return INTERSECT;
  }
  opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
  if (opqbc == INTERSECT) {
    return INTERSECT;
  }
  opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
  if (opqca == INTERSECT) {
    return 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 == SHAREEDGE) {
#ifdef SELF_CHECK
    assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX);
#endif
    // op is coincident with an edge of abc.
    return SHAREEDGE;
  }
  if (abcpq == SHAREEDGE) {
#ifdef SELF_CHECK
    assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX);
#endif
    // pq is coincident with an edge of abc.
    return SHAREEDGE;
  }
  if (abcqo == SHAREEDGE) {
#ifdef SELF_CHECK
    assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX);
#endif
    // qo is coincident with an edge of abc.
    return SHAREEDGE;
  }

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

  // They are disjoint.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insphere_sos()    Insphere test with symbolic perturbation.               //
//                                                                           //
// The input points a, b, c, and d should be non-coplanar. They must be ord- //
// ered so that they have a positive orientation (as defined by orient3d()), //
// or the sign of the result will be reversed.                               //
//                                                                           //
// Return a positive value if the point e lies inside the circumsphere of a, //
// b, c, and d; a negative value if it lies outside.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::insphere_sos(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
  int ia, int ib, int ic, int id, int ie)
{
  REAL det;

  det = insphere(pa, pb, pc, pd, pe);
  if (det != 0.0) {
    return det;
  }
  
  // det = 0.0, use symbolic perturbation.
  REAL *p[5], *tmpp;
  REAL sign, det_c, det_d;
  int idx[5], perm, tmp; 
  int n, i, j; 

  p[0] = pa; idx[0] = ia;
  p[1] = pb; idx[1] = ib;
  p[2] = pc; idx[2] = ic;
  p[3] = pd; idx[3] = id;
  p[4] = pe; idx[4] = ie;

  // Bubble sort the points by the increasing order of the indices.
  n = 5;
  perm = 0; // The number of total swaps.
  for (i = 0; i < n - 1; i++) {
    for (j = 0; j < n - 1 - i; j++) {
      if (idx[j + 1] < idx[j]) {  // compare the two neighbors.
        tmp = idx[j];         // swap idx[j] and idx[j + 1]
        idx[j] = idx[j + 1];
        idx[j + 1] = tmp;
        tmpp = p[j];         // swap p[j] and p[j + 1]
        p[j] = p[j + 1];
        p[j + 1] = tmpp;
        perm++;
      }
    }
  }

  sign = (perm % 2 == 0) ? 1.0 : -1.0; 
  det_c = orient3d(p[1], p[2], p[3], p[4]); // orient3d(b, c, d, e)
  if (det_c != 0.0) {
    return sign * det_c;
  }
  det_d = orient3d(p[0], p[2], p[3], p[4]); // orient3d(a, c, d, e)
  return -sign * det_d;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscollinear()    Check if three points are approximately collinear.       //
//                                                                           //
// 'eps' is a relative error tolerance.  The collinearity is determined by   //
// the value q = cos(theta), where theta is the angle between two vectors    //
// A->B and A->C.  They're collinear if 1.0 - q <= epspp.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps)
{
  REAL abx, aby, abz;
  REAL acx, acy, acz;
  REAL Lv, Lw, dd;
  REAL d, q;

  // Limit of two closed points.
  q = longest * eps;
  q *= q;

  abx = A[0] - B[0];
  aby = A[1] - B[1];
  abz = A[2] - B[2];
  acx = A[0] - C[0];
  acy = A[1] - C[1];
  acz = A[2] - C[2];
  Lv = abx * abx + aby * aby + abz * abz;
  // Is AB (nearly) indentical?
  if (Lv < q) return true;
  Lw = acx * acx + acy * acy + acz * acz;
  // Is AC (nearly) indentical?
  if (Lw < q) return true;
  dd = abx * acx + aby * acy + abz * acz;
  
  d = (dd * dd) / (Lv * Lw);
  if (d > 1.0) d = 1.0; // Rounding.
  q = 1.0 - sqrt(d); // Notice 0 < q < 1.0.
  
  return q <= eps;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscoplanar()    Check if four points are approximately coplanar.          //
//                                                                           //
// 'vol6' is six times of the signed volume of the tetrahedron formed by the //
// four points. 'eps' is the relative error tolerance.  The coplanarity is   //
// determined by the value: q = fabs(vol6) / L^3,  where L is the average    //
// edge length of the tet. They're coplanar if q <= eps.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::
iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps)
{
  REAL L, q;
  REAL x, y, z;  

  if (vol6 == 0.0) return true;

  x = k[0] - l[0];
  y = k[1] - l[1];
  z = k[2] - l[2];
  L = sqrt(x * x + y * y + z * z);
  x = l[0] - m[0];
  y = l[1] - m[1];
  z = l[2] - m[2];
  L += sqrt(x * x + y * y + z * z);
  x = m[0] - k[0];
  y = m[1] - k[1];
  z = m[2] - k[2];
  L += sqrt(x * x + y * y + z * z);
  x = k[0] - n[0];
  y = k[1] - n[1];
  z = k[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
  x = l[0] - n[0];
  y = l[1] - n[1];
  z = l[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
  x = m[0] - n[0];
  y = m[1] - n[1];
  z = m[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
#ifdef SELF_CHECK
  assert(L > 0.0);
#endif
  L /= 6.0;
  q = fabs(vol6) / (L * L * L);
  
  return q <= eps;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscospheric()    Check if five points are approximately coplanar.         //
//                                                                           //
// 'vol24' is the 24 times of the signed volume of the 4-dimensional simplex //
// formed by the five points. 'eps' is the relative tolerance. The cosphere  //
// case is determined by the value: q = fabs(vol24) / L^4,  where L is the   //
// average edge length of the simplex. They're cosphere if q <= eps.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::
iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps)
{
  REAL L, q;

  // A 4D simplex has 10 edges.
  L = distance(k, l);
  L += distance(l, m);
  L += distance(m, k);
  L += distance(k, n);
  L += distance(l, n);
  L += distance(m, n);
  L += distance(k, o);
  L += distance(l, o);
  L += distance(m, o);
  L += distance(n, o);
#ifdef SELF_CHECK
  assert(L > 0.0);
#endif
  L /= 10.0;
  q = fabs(vol24) / (L * L * L * L);

  return q < eps;
}

//
// End of geometric tests
//

//
// Begin of Geometric quantities calculators
//

// distance() computs the Euclidean distance between two points.
inline REAL tetgenmesh::distance(REAL* p1, REAL* p2)
{
  return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) +
              (p2[1] - p1[1]) * (p2[1] - p1[1]) +
              (p2[2] - p1[2]) * (p2[2] - p1[2]));
}

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

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

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

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

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

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shortdistance()    Returns the shortest distance from point p to a face.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3)
{
  REAL prj[3];

  projpt2face(p, e1, e2, e3, prj);
  return distance(p, prj);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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;
#ifdef SELF_CHECK
  assert(lenlen != 0.0);
#endif
  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;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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));
#ifdef SELF_CHECK
  assert(len != 0.0);
#endif
  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, &len);
#ifdef SELF_CHECK
  assert(len > 0.0);
#endif
  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];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facenormal()    Calculate the normal of a face given by three points.     //
//                                                                           //
// In general, the face normal can be calculate by the cross product of any  //
// pair of the three edge vectors.  However, if the three points are nearly  //
// collinear, the rounding error may harm the result. To choose a good pair  //
// of vectors is helpful to reduce the error.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen)
{
  REAL v1[3], v2[3];

  v1[0] = pb[0] - pa[0];
  v1[1] = pb[1] - pa[1];
  v1[2] = pb[2] - pa[2];
  v2[0] = pc[0] - pa[0];
  v2[1] = pc[1] - pa[1];
  v2[2] = pc[2] - pa[2];

  cross(v1, v2, n);
  if (nlen != (REAL *) NULL) {
    *nlen = sqrt(dot(n, n));
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edgeorthonormal()    Return the unit normal of an edge in a given plane.  //
//                                                                           //
// The edge is from e1 to e2,  the plane is defined by given an additional   //
// point op, which is non-collinear with the edge.  In addition, the side of //
// the edge in which op lies defines the positive position of the normal.    //
//                                                                           //
// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from  //
// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the //
// unit edge normal of e1e2 pointing to op is n = fn x v1.  Note, we should  //
// not change the position of fn and v1, otherwise, we get the edge normal   //
// pointing to the other side of op.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n)
{
  REAL v1[3], v2[3], fn[3];
  REAL len;

  // Get the edge vector v1.
  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  // Get the edge vector v2.
  v2[0] = op[0] - e1[0];
  v2[1] = op[1] - e1[1];
  v2[2] = op[2] - e1[2];
  // Get the face normal fn = v1 x v2.
  cross(v1, v2, fn);
  // Get the edge normal n pointing to op. n = fn x v1.
  cross(fn, v1, n);
  // Normalize the vector.
  len = sqrt(dot(n, n));
  n[0] /= len;
  n[1] /= len;
  n[2] /= len;
}

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

  return theta;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetalldihedral()    Get all (six) dihedral angles of a tet.               //
//                                                                           //
// The tet is given by its four corners a, b, c, and d. If 'cosdd' is not    //
// NULL, it returns the cosines of the 6 dihedral angles, the corresponding  //
// edges are: ab, bc, ca, ad, bd, and cd. If 'cosmaxd' (or 'cosmind') is not //
// NULL, it returns the cosine of the maximal (or minimal) dihedral angle.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  // Get four normals of faces of the tet.
  tetallnormal(pa, pb, pc, pd, N, NULL);
  // Normalize the normals.
  for (i = 0; i < 4; i++) {
    len = sqrt(dot(N[i], N[i]));
    if (len != 0.0) {
      for (j = 0; j < 3; j++) N[i][j] /= len;
    }
  }

  for (i = 0; i < 6; i++) {
    switch (i) {
    case 0: f1 = 2; f2 = 3; break; // edge ab.
    case 1: f1 = 0; f2 = 3; break; // edge bc.
    case 2: f1 = 1; f2 = 3; break; // edge ca.
    case 3: f1 = 1; f2 = 2; break; // edge ad.
    case 4: f1 = 2; f2 = 0; break; // edge bd.
    case 5: f1 = 0; f2 = 1; break; // edge cd.
    }
    cosd = -dot(N[f1], N[f2]);
    if (cosdd) cosdd[i] = cosd;
    if (i == 0) {
      if (cosmaxd) *cosmaxd = cosd;
      if (cosmind) *cosmind = cosd;
    } else {
      if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd;
      if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind;
    }
  }
}

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

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

  // get the entries of A[3][3].
  for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i];  // d->a vec
  for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i];  // d->b vec
  for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i];  // d->c vec
  // Compute the inverse of matrix A, to get 3 normals of the 4 faces.
  lu_decmp(A, 3, indx, &D, 0);     // Decompose the matrix just once.
  if (volume != NULL) {
    // Get the volume of the tet.
    *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0;
  }
  for (j = 0; j < 3; j++) {
    for (i = 0; i < 3; i++) rhs[i] = 0.0;
    rhs[j] = 1.0;  // Positive means the inside direction
    lu_solve(A, 3, indx, rhs, 0);
    for (i = 0; i < 3; i++) N[j][i] = rhs[i];
  }
  // Get the fourth normal by summing up the first three.
  for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
}

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

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

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

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

  return sqrt(radius2) * minheightinv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// circumsphere()    Calculate the smallest circumsphere (center and radius) //
//                   of the given three or four points.                      //
//                                                                           //
// The circumsphere of four points (a tetrahedron) is unique if they are not //
// degenerate. If 'pd = 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 indicated the points are degenrate.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2];
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2];
  if (pd != 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;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// inscribedsphere()    Compute the radius and center of the biggest         //
//                      inscribed sphere of a given tetrahedron.             //
//                                                                           //
// The tetrahedron is given by its four points, it must not be degenerate.   //
// The center and radius are returned in 'cent' and 'radius' respectively if //
// they are not NULLs.                                                       //
//                                                                           //
// Geometrical fact. For any simplex in d dimension,                         //
//   r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1);                                //
// where r is the radius of inscribed ball, and h is the height of each side //
// of the simplex. The value of 'r/h' is just the barycenter coordinates of  //
// each vertex of the simplex. Therefore, we can compute the radius and      //
// center of the smallest inscribed ball as following equations:             //
//   r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn);          (1)                      //
//   C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn;   (2)                      //
// where C is the vector of center, P1, P2, .. Pn are vectors of vertices.   //
// Here (2) contains n linear equations with n variables.  (h, P) must be a  //
// pair, h is the height from P to its opposite face.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd,
  REAL* cent, REAL* radius)
{
  REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face.
  REAL rd;
  int i;  

  // Get the all normals of the tet.
  tetallnormal(pa, pb, pc, pd, N, NULL);
  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]));
  }
  // Compute the radius use eq. (1).
  rd = 1.0 / (H[0] + H[1] + H[2] + H[3]);
  if (radius != (REAL*) NULL) *radius = rd;
  if (cent != (REAL*) NULL) {
    // Compute the center use eq. (2).
    cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]);
    cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]);
    cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// rotatepoint()    Create a point by rotating an existing point.            //
//                                                                           //
// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc  //
// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. //
// The rotation is according with right-hand rule, i.e., use your right-hand //
// to grab the axis with your thumber pointing to its positive direction,    //
// your fingers indicate the rotating direction.                             //
//                                                                           //
// The rotating steps are the following:                                     //
//   1. Translate vector 'p1->p2' to origin, M1;                             //
//   2. Rotate vector around the Y-axis until it lies in the YZ plane, M2;   //
//   3. Rotate vector around the X-axis until it lies on the Z axis, M3;     //
//   4. Perform the rotation of 'p' around the z-axis, M4;                   //
//   5. Undo Step 3, M5;                                                     //
//   6. Undo Step 2, M6;                                                     //
//   7. Undo Step 1, M7;                                                     //
// Use matrix multiplication to combine the above sequences, we get:         //
//   p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2)
{
  REAL T[4][4], pp0[4], p0t[4], p2t[4];
  REAL roty, rotx, alphaR, projlen;
  REAL dx, dy, dz;

  initm44(1, 0, 0, -p1[0],
          0, 1, 0, -p1[1],
          0, 0, 1, -p1[2],
          0, 0, 0, 1, T);
  pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 1
  pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0;
  m4xv4(p2t, T, pp0); // Step 1

  // Get the rotation angle around y-axis;
  dx = p2t[0];
  dz = p2t[2];
  projlen = sqrt(dx * dx + dz * dz);
  if (projlen <= (b->epsilon * 1e-2) * longest) {
    roty = 0;
  } else {
    roty = acos(dz / projlen);
    if (dx < 0) {
      roty = -roty;
    }
  }

  initm44(cos(-roty), 0, sin(-roty), 0,
          0, 1, 0, 0,
          -sin(-roty), 0, cos(-roty), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 2
  pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
  m4xv4(p2t, T, pp0); // Step 2

  // Get the rotation angle around x-axis
  dy = p2t[1];
  dz = p2t[2];
  projlen = sqrt(dy * dy + dz * dz);
  if (projlen <= (b->epsilon * 1e-2) * longest) {
    rotx = 0;
  } else {
    rotx = acos(dz / projlen);
    if (dy < 0) {
      rotx = -rotx;
    }
  }
    
  initm44(1, 0, 0, 0,
          0, cos(rotx), -sin(rotx), 0,
          0, sin(rotx), cos(rotx), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 3
  // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
  // m4xv4(p2t, T, pp0); // Step 3

  alphaR = rotangle;
  initm44(cos(alphaR), -sin(alphaR), 0, 0,
          sin(alphaR), cos(alphaR), 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 4

  initm44(1, 0, 0, 0,
          0, cos(-rotx), -sin(-rotx), 0,
          0, sin(-rotx), cos(-rotx), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 5

  initm44(cos(roty), 0, sin(roty), 0,
          0, 1, 0, 0,
          -sin(roty), 0, cos(roty), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 6

  initm44(1, 0, 0, p1[0],
          0, 1, 0, p1[1],
          0, 0, 1, p1[2],
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 7  

  p[0] = p0t[0];
  p[1] = p0t[1];
  p[2] = p0t[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// spherelineint()    3D line sphere (or circle) intersection.               //
//                                                                           //
// The line is given by two points p1, and p2, the sphere is centered at c   //
// with radius r.  This function returns a pointer array p which first index //
// indicates the number of intersection point, followed by coordinate pairs. //
//                                                                           //
// The following code are adapted from: http://astronomy.swin.edu.au/pbourke //
// /geometry/sphereline. Paul Bourke pbourke@swin.edu.au                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7])
{
  REAL x1, y1, z1; //  P1 coordinates (point of line)
  REAL x2, y2, z2; //  P2 coordinates (point of line)
  REAL x3, y3, z3, r; //  P3 coordinates and radius (sphere)
  REAL a, b, c, mu, i ;

  x1 = p1[0]; y1 = p1[1]; z1 = p1[2];
  x2 = p2[0]; y2 = p2[1]; z2 = p2[2];
  x3 = C[0];  y3 = C[1];  z3 = C[2];
  r = R;
  
  a =   (x2 - x1) * (x2 - x1) 
      + (y2 - y1) * (y2 - y1) 
      + (z2 - z1) * (z2 - z1);
  b = 2 * ( (x2 - x1) * (x1 - x3)
          + (y2 - y1) * (y1 - y3)
          + (z2 - z1) * (z1 - z3) ) ;
  c =   (x3 * x3) + (y3 * y3) + (z3 * z3)
      + (x1 * x1) + (y1 * y1) + (z1 * z1)
      - 2 * (x3 * x1 + y3 * y1 + z3 * z1) - (r * r) ;
  i = b * b - 4 * a * c ;

  if (i < 0.0) {
    // no intersection
    p[0] = 0.0;
  } else if (i == 0.0) {
    // one intersection
    p[0] = 1.0;
    mu = -b / (2 * a) ;
    p[1] = x1 + mu * (x2 - x1);
    p[2] = y1 + mu * (y2 - y1);
    p[3] = z1 + mu * (z2 - z1);
  } else {
    // two intersections
    p[0] = 2.0;
    // first intersection
    mu = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a);
    p[1] = x1 + mu * (x2 - x1);
    p[2] = y1 + mu * (y2 - y1);
    p[3] = z1 + mu * (z2 - z1);
    // second intersection
    mu = (-b - sqrt((b * b) - 4 * a * c)) / (2 * a);
    p[4] = x1 + mu * (x2 - x1);
    p[5] = y1 + mu * (y2 - y1);
    p[6] = z1 + mu * (z2 - z1);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// linelineint()    Calculate the shortest line between two lines in 3D.     //
//                                                                           //
// Two 3D lines generally don't intersect at a point, they may be parallel ( //
// no intersections), or coincident (infinite intersections) but most often  //
// only their projections onto a plane intersect. If they don't exactly int- //
// ersect at a point they can be connected by a line segment, the shortest   //
// segment is unique and is often considered to be their intersection in 3D. //
//                                                                           //
// The following code are adapted from: http://astronomy.swin.edu.au/pbourke //
// /geometry/lineline3d. Paul Bourke pbourke@swin.edu.au                     //
//                                                                           //
// Calculate the line segment PaPb that is the shortest route between two    //
// lines P1P2 and P3P4. This function returns a pointer array p which first  //
// index indicates there exists solution or not, 0 means no solution, 1 meas //
// has solution followed by two coordinate pairs.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7])
{
  REAL p13[3], p43[3], p21[3];
  REAL d1343, d4321, d1321, d4343, d2121;
  REAL numer, denom;
  REAL mua, mub;

  p13[0] = p1[0] - p3[0];
  p13[1] = p1[1] - p3[1];
  p13[2] = p1[2] - p3[2];
  p43[0] = p4[0] - p3[0];
  p43[1] = p4[1] - p3[1];
  p43[2] = p4[2] - p3[2];
  if (p43[0] == 0.0 && p43[1] == 0.0 && p43[2] == 0.0) {
    p[0] = 0.0;
    return;
  }

  p21[0] = p2[0] - p1[0];
  p21[1] = p2[1] - p1[1];
  p21[2] = p2[2] - p1[2];
  if (p21[0] == 0.0 && p21[1] == 0.0 && p21[2] == 0.0) {
    p[0] = 0.0;
    return;
  }

  d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2];
  d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2];
  d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2];
  d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2];
  d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2];

  denom = d2121 * d4343 - d4321 * d4321;
  if (denom == 0.0) {
    p[0] = 0.0;
    return;
  }
  numer = d1343 * d4321 - d1321 * d4343;
  mua = numer / denom;
  mub = (d1343 + d4321 * mua) / d4343;

  p[0] = 1.0;
  p[1] = p1[0] + mua * p21[0];
  p[2] = p1[1] + mua * p21[1];
  p[3] = p1[2] + mua * p21[2];
  p[4] = p3[0] + mub * p43[0];
  p[5] = p3[1] + mub * p43[1];
  p[6] = p3[2] + mub * p43[2];
}

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

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

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

//
// End of Geometric quantities calculators
//

//
// Begin of memory management routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dummyinit()    Initialize the tetrahedron that fills "outer space" and    //
//                the omnipresent subface.                                   //
//                                                                           //
// The tetrahedron that fills "outer space" called 'dummytet', is pointed to //
// by every tetrahedron and subface on a boundary (be it outer or inner) of  //
// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron //
// on the convex hull(until the holes and concavities are carved), making it //
// possible to find a starting tetrahedron for point location.               //
//                                                                           //
// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or  //
// subface that doesn't have a full complement of real subface to point to.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::dummyinit(int tetwords, int shwords)
{
  size_t alignptr;

  // Set up 'dummytet', the 'tetrahedron' that occupies "outer space".
  dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron)
                                          + tetrahedrons->alignbytes];
  // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary.
  alignptr = (size_t) dummytetbase;
  dummytet = (tetrahedron *)
    (alignptr + (size_t) tetrahedrons->alignbytes
     - (alignptr % (size_t) tetrahedrons->alignbytes));
  // Initialize the four adjoining tetrahedra to be "outer space". These
  //   will eventually be changed by various bonding operations, but their
  //   values don't really matter, as long as they can legally be
  //   dereferenced.
  dummytet[0] = (tetrahedron) dummytet;
  dummytet[1] = (tetrahedron) dummytet;
  dummytet[2] = (tetrahedron) dummytet;
  dummytet[3] = (tetrahedron) dummytet;
  // Four null vertex points.
  dummytet[4] = (tetrahedron) NULL;
  dummytet[5] = (tetrahedron) NULL;
  dummytet[6] = (tetrahedron) NULL;
  dummytet[7] = (tetrahedron) NULL;

  if (b->useshelles) {
    // Set up 'dummysh', the omnipresent "subface" pointed to by any
    //   tetrahedron side or subface end that isn't attached to a real
    //   subface.
    dummyshbase = (shellface *) new char[shwords * sizeof(shellface)
                                         + subfaces->alignbytes];
    // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary.
    alignptr = (size_t) dummyshbase;
    dummysh = (shellface *)
      (alignptr + (size_t) subfaces->alignbytes
       - (alignptr % (size_t) subfaces->alignbytes));
    // Initialize the three adjoining subfaces to be the omnipresent
    //   subface. These will eventually be changed by various bonding
    //   operations, but their values don't really matter, as long as they
    //   can legally be dereferenced.
    dummysh[0] = (shellface) dummysh;
    dummysh[1] = (shellface) dummysh;
    dummysh[2] = (shellface) dummysh;
    // Three null vertex points.
    dummysh[3] = (shellface) NULL;
    dummysh[4] = (shellface) NULL;
    dummysh[5] = (shellface) NULL;
    // Initialize the two adjoining tetrahedra to be "outer space".
    dummysh[6] = (shellface) dummytet;
    dummysh[7] = (shellface) dummytet;
    // Initialize the three adjoining subsegments to be "out boundary".
    dummysh[8]  = (shellface) dummysh;
    dummysh[9]  = (shellface) dummysh;
    dummysh[10] = (shellface) dummysh;
    // Initialize the pointer to badface structure.
    dummysh[11] = (shellface) NULL;
    // Initialize the four adjoining subfaces of 'dummytet' to be the
    //   omnipresent subface.
    dummytet[8 ] = (tetrahedron) dummysh;
    dummytet[9 ] = (tetrahedron) dummysh;
    dummytet[10] = (tetrahedron) dummysh;
    dummytet[11] = (tetrahedron) dummysh;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initializepools()    Calculate the sizes of the point, tetrahedron, and   //
//                      subface. Initialize their memory pools.              //
//                                                                           //
// This routine also computes the indices 'pointmarkindex', 'point2simindex',//
// and 'point2pbcptindex' used to find values within each point;  computes   //
// indices 'highorderindex', 'elemattribindex', and 'volumeboundindex' used  //
// to find values within each tetrahedron.                                   //
//                                                                           //
// There are two types of boundary elements, which are subfaces and subsegs, //
// they are stored in seperate pools. However, the data structures of them   //
// are the same.  A subsegment can be regarded as a degenerate subface, i.e.,//
// one of its three corners is not used. We set the apex of it be 'NULL' to  //
// distinguish it's a subsegment.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initializepools()
{
  enum wordtype wtype;
  int pointsize, elesize, shsize;

  // Default checkpbc = 0;
  if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) {
    checkpbcs = 1;
  }
  // Default varconstraint = 0;
  if (in->segmentconstraintlist || in->facetconstraintlist) {
    varconstraint = 1;
  }

  // The index within each point at which its metric tensor is found. It is
  //   saved directly after the list of point attributes.
  pointmtrindex = 3 + in->numberofpointattributes;
  // Decide the size (1, 3, or 6) of the metric tensor.
  if (b->metric) {
    // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
    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;
  }
  // 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) {
    // Increase the point size by three pointers, which are:
    //   - a pointer to a tet, read by point2tet();
    //   - a pointer to a subface/subsegment , read by point2sh();
    //   - a pointer to a parent point, read by point2ppt()).
    if (b->metric) {
      // Increase one pointer to a tet of the background mesh.
      pointsize = (point2simindex + 4) * sizeof(tetrahedron);
    } else {
      pointsize = (point2simindex + 3) * sizeof(tetrahedron);
    }
    // The index within each point at which a pbc point is found.
    point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1)
                     / sizeof(tetrahedron);
    if (checkpbcs) {
      // Increase the size by one pointer to a corresponding pbc point,
      //   read by point2pbcpt().
      pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron);
    }
  } else {
    pointsize = point2simindex * sizeof(tetrahedron);
  }
  // The index within each point at which the boundary marker is found,
  //   Ensure the point marker is aligned to a sizeof(int)-byte address.
  pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
  // Now point size is the ints (inidcated by pointmarkindex) plus:
  //   - an integer for boundary marker;
  //   - an integer for vertex type;
  pointsize = (pointmarkindex + 2) * sizeof(int);
  // Decide the wordtype used in vertex pool.
  wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER;
  // Initialize the pool of vertices.
  points = new memorypool(pointsize, VERPERBLOCK, wtype, 0);

  // The number of bytes occupied by a tetrahedron.  There are four pointers
  //   to other tetrahedra, four pointers to corners, and possibly four
  //   pointers to subfaces.
  elesize = (8 + b->useshelles * 6) * sizeof(tetrahedron);
  // If Voronoi diagram is wanted, make sure we have additional space.
  if (b->voroout && (b->useshelles == 0)) {
    elesize = (8 + 4) * sizeof(tetrahedron);
  }
  // The index within each element at which its attributes are found, where
  //   the index is measured in REALs. 
  elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
  // The index within each element at which the maximum voulme bound is
  //   found, where the index is measured in REALs.  Note that if the
  //   `b->regionattrib' flag is set, an additional attribute will be added.
  volumeboundindex = elemattribindex + in->numberoftetrahedronattributes
                   + (b->regionattrib > 0);
  // If element attributes or an constraint are needed, increase the number
  //   of bytes occupied by an element.
  if (b->varvolume) {
    elesize = (volumeboundindex + 1) * sizeof(REAL);
  } else if (in->numberoftetrahedronattributes + b->regionattrib > 0) {
    elesize = volumeboundindex * sizeof(REAL);
  }
  // If element neighbor graph is requested (-n switch), an additional
  //   integer is allocated for each element.
  elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int);
  if (b->neighout || b->voroout) {
    elesize = (elemmarkerindex + 1) * sizeof(int);
  }
  // If -o2 switch is used, an additional pointer pointed to the list of
  //   higher order nodes is allocated for each element.
  highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
  if (b->order == 2) {
    elesize = (highorderindex + 1) * sizeof(tetrahedron);
  }
  // Having determined the memory size of an element, initialize the pool.
  tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8);

  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, and one to a badface.
    shsize = 12 * 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 (b->quality && varconstraint) {
      shsize = (areaboundindex + 1) * sizeof(REAL);
    } else {
      shsize = areaboundindex * sizeof(REAL);
    }
    // The index within subface at which the facet marker is found. Ensure
    //   the marker is aligned to a sizeof(int)-byte address.
    shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int);
    // Increase the number of bytes by two or three integers, one for facet
    //   marker, one for shellface type, and optionally one for pbc group.
    shsize = (shmarkindex + 2 + checkpbcs) * sizeof(int);
    // 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, SUBPERBLOCK, POINTER, 8);
    // Initialize the pool of subsegments. The subsegment's record is same
    //   with subface.
    subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8);
    // Initialize the "outer space" tetrahedron and omnipresent subface.
    dummyinit(tetrahedrons->itemwords, subfaces->itemwords);
  } else {
    // Initialize the "outer space" tetrahedron.
    dummyinit(tetrahedrons->itemwords, 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;
  dyingtetrahedron[5] = (tetrahedron) NULL;
  dyingtetrahedron[6] = (tetrahedron) NULL;
  dyingtetrahedron[7] = (tetrahedron) NULL;
  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[7] == (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;
  dyingsh[4] = (shellface) NULL;
  dyingsh[5] = (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;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// badfacedealloc()    Deallocate space for a badface, marking it dead.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying)
{
  // Set badface's forg to NULL. This makes it possible to detect dead
  //   ones when traversing the list of all items.
  dying->forg = (point) NULL;
  pool->dealloc((void *) dying);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// badfacetraverse()    Traverse the pools, skipping dead ones.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool)
{
  badface *newsh;

  do {
    newsh = (badface *) pool->traverse();
    if (newsh == (badface *) NULL) {
      return (badface *) NULL;
    }
  } while (newsh->forg == (point) NULL);               // Skip dead ones.
  return newsh;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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] = (tetrahedron) dummytet;
  newtet->tet[1] = (tetrahedron) dummytet;
  newtet->tet[2] = (tetrahedron) dummytet;
  newtet->tet[3] = (tetrahedron) dummytet;
  // Four NULL vertices.
  newtet->tet[4] = (tetrahedron) NULL;
  newtet->tet[5] = (tetrahedron) NULL;
  newtet->tet[6] = (tetrahedron) NULL;
  newtet->tet[7] = (tetrahedron) NULL;
  // Initialize the four adjoining subfaces to be the omnipresent subface.
  if (b->useshelles) {
    newtet->tet[8 ] = (tetrahedron) dummysh;
    newtet->tet[9 ] = (tetrahedron) dummysh;
    newtet->tet[10] = (tetrahedron) dummysh;
    newtet->tet[11] = (tetrahedron) dummysh;
    newtet->tet[12] = (tetrahedron) dummysh;
    newtet->tet[13] = (tetrahedron) dummysh;
  }
  for (int i = 0; i < in->numberoftetrahedronattributes; i++) {
    setelemattribute(newtet->tet, i, 0.0);
  }
  if (b->varvolume) {
    setvolumebound(newtet->tet, -1.0);
  }
  // Initialize the location and version to be Zero.
  newtet->loc = 0;
  newtet->ver = 0;
}

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

void tetgenmesh::makeshellface(memorypool *pool, face *newface)
{
  newface->sh = (shellface *) pool->alloc();
  //Initialize the three adjoining subfaces to be the omnipresent subface.
  newface->sh[0] = (shellface) dummysh;
  newface->sh[1] = (shellface) dummysh;
  newface->sh[2] = (shellface) dummysh;
  // Three NULL vertices.
  newface->sh[3] = (shellface) NULL;
  newface->sh[4] = (shellface) NULL;
  newface->sh[5] = (shellface) NULL;
  // Initialize the two adjoining tetrahedra to be "outer space".
  newface->sh[6] = (shellface) dummytet;
  newface->sh[7] = (shellface) dummytet;
  // Initialize the three adjoining subsegments to be the omnipresent
  //   subsegments.
  newface->sh [8] = (shellface) dummysh;
  newface->sh [9] = (shellface) dummysh;
  newface->sh[10] = (shellface) dummysh;
  // Initialize the pointer to badface structure.
  newface->sh[11] = (shellface) NULL;
  if (b->quality && varconstraint) {
    // Initialize the maximum area bound.
    setareabound(*newface, 0.0);
  }
  // Set the boundary marker to zero.
  setshellmark(*newface, 0);
  // Set the type.
  setshelltype(*newface, NSHARP);
  if (checkpbcs) {
    // Set the pbcgroup be ivalid.
    setshellpbcgroup(*newface, -1);
  }
  // Initialize the version to be Zero.
  newface->shver = 0;
}

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

void tetgenmesh::makepoint(point* pnewpoint)
{
  int ptmark, i;

  *pnewpoint = (point) points->alloc();
  // Initialize three coordinates.
  (*pnewpoint)[0] = 0.0;
  (*pnewpoint)[1] = 0.0;
  (*pnewpoint)[2] = 0.0;
  // Initialize the list of user-defined attributes.
  for (i = 0; i < in->numberofpointattributes; i++) {
    (*pnewpoint)[3 + i] = 0.0;
  }
  // Initialize the metric tensor.
  for (i = 0; i < sizeoftensor; i++) {
    (*pnewpoint)[pointmtrindex + i] = 0.0;
  }
  if (b->plc || b->refine) {
    // Initialize the point-to-simplex filed.
    setpoint2tet(*pnewpoint, NULL);
    setpoint2sh(*pnewpoint, NULL);
    setpoint2ppt(*pnewpoint, NULL);
    if (b->metric) {
      setpoint2bgmtet(*pnewpoint, NULL);
    }
    if (checkpbcs) {
      // Initialize the other pointer to its pbc point.
      setpoint2pbcpt(*pnewpoint, NULL);
    }
  }
  // Initialize the point marker (starting from in->firstnumber).
  ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
  setpointmark(*pnewpoint, ptmark);
  // Initialize the point type.
  setpointtype(*pnewpoint, UNUSEDVERTEX);
}

//
// End of memory management routines
//

//
// Begin of point location routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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;
  }
  // Old function.
  // randomseed = (randomseed * 1366l + 150889l) % 714025l;
  // return randomseed / (714025l / choices + 1);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// distance2()    Returns the square "distance" of a tetrahedron to point p. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::distance2(tetrahedron* tetptr, point p)
{
  point p1, p2, p3, p4;
  REAL dx, dy, dz;

  p1 = (point) tetptr[4];
  p2 = (point) tetptr[5];
  p3 = (point) tetptr[6];
  p4 = (point) tetptr[7];

  dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]);
  dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]);
  dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]);

  return dx * dx + dy * dy + dz * dz;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// preciselocate()    Find a simplex containing a given point.               //
//                                                                           //
// This routine implements the simple Walk-through point location algorithm. //
// 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:                      //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies on a mesh edge.  'searchtet' is a    //
//     handle whose primary edge is the edge on which the point lies.        //
//   - Returns ONFACE if the point lies strictly within a face. 'searchtet'  //
//     is a handle whose primary face is the face on which the point lies.   //
//   - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron.  //
//     'searchtet' is a handle on the tetrahedron that contains the point.   //
//   - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a  //
//     handle whose location is the face the point is to 'above' of.         //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//                                                                           //
// If 'maxtetnumber' > 0, stop the searching process if the number of passed //
// tets is larger than it and return OUTSIDE.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt,
  triface* searchtet, long maxtetnumber)
{
  triface backtracetet;
  triface walkthroface;
  point forg, fdest, fapex, toppo;
  REAL ori1, ori2, ori3, ori4;
  long tetnumber;
  int side;

  if (isdead(searchtet)) searchtet->tet = dummytet;
  if (searchtet->tet == dummytet) {
    searchtet->loc = 0;
    symself(*searchtet);
  }
  // 'searchtet' should be a valid tetrahedron now.
#ifdef SELF_CHECK
  // assert(!isdead(searchtet) && (searchtet->tet != dummytet));
#endif
  if (isdead(searchtet)) {
    printf("Warning:  Point location failed.\n");
    return OUTSIDE;
  }

  searchtet->ver = 0; // Keep in CCW edge ring.
  // Find a face of 'searchtet' such that the 'searchpt' lies strictly
  //   above it.  Such face should always exist.
  for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) {
    forg = org(*searchtet);
    fdest = dest(*searchtet);
    fapex = apex(*searchtet);
    ori1 = orient3d(forg, fdest, fapex, searchpt);
    if (ori1 < 0.0) break;
  }
#ifdef SELF_CHECK
  assert(searchtet->loc < 4);
#endif

  // Define 'tetnumber' for exit the loop when it's running endless.
  tetnumber = 0l;
  while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) {
    // Check if we are reaching the boundary of the triangulation.
    if (searchtet->tet == dummytet) {
      *searchtet = backtracetet;
      return OUTSIDE;
    }
    // Initialize the face for returning the walk-through face.
    walkthroface.tet = (tetrahedron *) NULL;
    // Adjust the edge ring, so that 'ori1 < 0.0' holds.
    searchtet->ver = 0;
    // 'toppo' remains unchange for the following orientation tests.
    toppo = oppo(*searchtet);
    // Check the three sides of 'searchtet' to find the face through which
    //   we can walk next.
    for (side = 0; side < 3; side++) {
      forg = org(*searchtet);
      fdest = dest(*searchtet);
      ori2 = orient3d(forg, fdest, toppo, searchpt);
      if (ori2 == 0.0) {
        // They are coplanar, check if 'searchpt' lies inside, or on an edge,
        //   or coindice with a vertex of face (forg, fdest, toppo). 
        fapex = apex(*searchtet);
        ori3 = orient3d(fdest, fapex, toppo, searchpt);
        if (ori3 < 0.0) {
          // Outside the face (fdest, fapex, toppo), walk through it.
          enextself(*searchtet);
          fnext(*searchtet, walkthroface);
          break;
        }
        ori4 = orient3d(fapex, forg, toppo, searchpt);
        if (ori4 < 0.0) {
          // Outside the face (fapex, forg, toppo), walk through it.
          enext2self(*searchtet);
          fnext(*searchtet, walkthroface);
          break;
        }
        // Remember, ori1 < 0.0, which means 'searchpt' will not on edge
        //   (forg, fdest) or on vertex forg or fdest.
#ifdef SELF_CHECK
        assert(ori1 < 0.0);
#endif
        // The rest possible cases are: 
        //   (1) 'searchpt' lies on edge (fdest, toppo);
        //   (2) 'searchpt' lies on edge (toppo, forg);
        //   (3) 'searchpt' coincident with toppo;
        //   (4) 'searchpt' lies inside face (forg, fdest, toppo).
        fnextself(*searchtet);
        if (ori3 == 0.0) {
          if (ori4 == 0.0) {
            // Case (4).
            enext2self(*searchtet);
            return ONVERTEX;
          } else {
            // Case (1).
            enextself(*searchtet);
            return ONEDGE;
          }
        }
        if (ori4 == 0.0) {
          // Case (2).
          enext2self(*searchtet);
          return ONEDGE;
        }
        // Case (4).
        return ONFACE;
      } else if (ori2 < 0.0) {
        // Outside the face (forg, fdest, toppo), walk through it.
        fnext(*searchtet, walkthroface);
        break;
      }
      // Go to check next side.
      enextself(*searchtet);
    }
    if (side >= 3) {
      // Found! Inside tetrahedron.
      return INTETRAHEDRON;
    }
    // We walk through the face 'walkthroface' and continue the searching.
#ifdef SELF_CHECK
    assert(walkthroface.tet != (tetrahedron *) NULL);
#endif
    // Store the face handle in 'backtracetet' before we take the real walk.
    //   So we are able to restore the handle to 'searchtet' if we are
    //   reaching the outer boundary.
    backtracetet = walkthroface;
    sym(walkthroface, *searchtet);    
    tetnumber++;
  }

  // Should never be here.
  // printf("Internal error in preciselocate(): Point location failed.\n");
  // internalerror();
  return OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locate()    Find a simplex containing a given point.                      //
//                                                                           //
// This routine implements Muecke's Jump-and-walk point location algorithm.  //
// It improves the simple walk-through by "jumping" to a good starting point //
// via random sampling.  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 barycenter is closest to the point we are searcing for.  Having chosen //
// the starting tetrahedron, the simple Walk-through algorithm is used to do //
// the real walking.                                                         //
//                                                                           //
// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, //
// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding   //
// to that value. See the introduction part of preciselocate() for detail.   //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

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

  // 'searchtet' should be a valid tetrahedron.
  if (isdead(searchtet)) {
    searchtet->tet = dummytet;
  }
  if (searchtet->tet == dummytet) {
    // This is an 'Outer Space' handle, get a hull tetrahedron.
    searchtet->loc = 0;
    symself(*searchtet);
  }
#ifdef SELF_CHECK
  // assert(!isdead(searchtet));
#endif
  if (isdead(searchtet)) {
    printf("Warning:  Point location failed.\n");
    return OUTSIDE;
  }
  
  // Get the distance from the suggested starting tet to the point we seek.
  searchdist = distance2(searchtet->tet, searchpt);

  // If a recently encountered tetrahedron has been recorded and has not
  //   been deallocated, test it as a good starting point.
  if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) {
    dist = distance2(recenttet.tet, searchpt);
    if (dist < searchdist) {
      *searchtet = recenttet;
      searchdist = dist;
    }
  }

  // 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. The next bit of code assumes
  //   that the number of tetrahedra increases monotonically.
  while (SAMPLEFACTOR * samples * samples * samples * samples <
         tetrahedrons->items) {
    samples++;
  }
  // Find how much blocks in current tet pool.
  tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK;
  // Find the average samles per block. Each block at least have 1 sample.
  samplesperblock = 1 + (samples / tetblocks);
  sampleblocks = samples / samplesperblock;
  sampleblock = tetrahedrons->firstblock;
  for (i = 0; i < sampleblocks; i++) {
    alignptr = (size_t) (sampleblock + 1);
    firsttet = (tetrahedron *)
               (alignptr + (size_t) tetrahedrons->alignbytes
               - (alignptr % (size_t) tetrahedrons->alignbytes));
    for (j = 0; j < samplesperblock; j++) {
      if (i == tetblocks - 1) {
        // This is the last block.
        samplenum = randomnation((int)
                      (tetrahedrons->maxitems - (i * ELEPERBLOCK)));
      } else {
        samplenum = randomnation(ELEPERBLOCK);
      }
      tetptr = (tetrahedron *)
               (firsttet + (samplenum * tetrahedrons->itemwords));
      if (tetptr[4] != (tetrahedron) NULL) {
        dist = distance2(tetptr, searchpt);
        if (dist < searchdist) {
          searchtet->tet = tetptr;
          searchdist = dist;
        }
      }
    }
    sampleblock = (void **) *sampleblock;
  }
  
  // Call simple walk-through to locate the point.
  return preciselocate(searchpt, searchtet, tetrahedrons->items); 
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocate()    Adjust the precise location of a vertex.                //
//                                                                           //
// 'precise' is the value returned from preciselocate().  It indicates the   //
// exact location of the point 'searchpt' with respect to the tetrahedron    //
// 'searchtet'.  'epspp' is a given relative tolerance.                      //
//                                                                           //
// This routine re-evaluates the orientations of searchpt with respect to    //
// the four sides of searchtet. Detects the coplanarities by additinal tests //
// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, //
// we can save one or two orientation tests.                                 //
//                                                                           //
// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, //
// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding   //
// to that value. See the introduction part of preciselocate() for detail.   //
//                                                                           //
// WARNING:  This routine detect degenerate case using relative tolerance.   //
// It is better used after locate() or preciselocate().  For general inputs, //
// it may not able to tell the correct location.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt,
  triface* searchtet, enum locateresult precise, REAL epspp)
{
  point torg, tdest, tapex, toppo;
  REAL s1, s2, s3, s4;

  // For the given 'searchtet', the orientations tests are:
  //  s1: (tdest, torg, tapex, searchpt);
  //  s2: (torg, tdest, toppo, searchpt);
  //  s3: (tdest, tapex, toppo, searchpt);
  //  s4: (tapex, torg, toppo, searchpt);
  adjustedgering(*searchtet, CCW);
  torg = org(*searchtet);
  tdest = dest(*searchtet);
  tapex = apex(*searchtet);
  toppo = oppo(*searchtet);

  switch (precise) {
  case ONVERTEX:
    // This case we don't need do any further test.
    return ONVERTEX;
  case ONEDGE:
    // (torg, tdest);
    s1 = 0.0;
    s2 = 0.0;
    break;
  case ONFACE:
    // (tdest, torg, tapex);
    s1 = 0.0;
    s2 = orient3d(torg, tdest, toppo, searchpt);
    break;
  default: // INTETRAHEDRON or OUTSIDE
    s1 = orient3d(tdest, torg, tapex, searchpt);
    s2 = orient3d(torg, tdest, toppo, searchpt);
  }
  
  if (s1 != 0.0) {
    if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) {
      s1 = 0.0;
    }
  }
  if (s1 < 0.0) {
    return OUTSIDE;
  }

  if (s2 != 0.0) {
    if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) {
      s2 = 0.0;
    }
  }
  if (s2 < 0.0) {
    fnextself(*searchtet);
    return OUTSIDE;
  }

  s3 = orient3d(tdest, tapex, toppo, searchpt);
  if (s3 != 0.0) {
    if (iscoplanar(tdest, tapex, toppo, searchpt, s3, epspp)) {
      s3 = 0.0;
    }
  }
  if (s3 < 0.0) {
    enextfnextself(*searchtet);
    return OUTSIDE;
  }

  s4 = orient3d(tapex, torg, toppo, searchpt);
  if (s4 != 0.0) {
    if (iscoplanar(tapex, torg, toppo, searchpt, s4, epspp)) {
      s4 = 0.0;
    }
  }
  if (s4 < 0.0) {
    enext2fnextself(*searchtet);
    return OUTSIDE;
  }

  // Determine degenerate cases.
  if (s1 == 0.0) {
    if (s2 == 0.0) {
      if (s3 == 0.0) {
        // On tdest.
        enextself(*searchtet);
        return ONVERTEX;
      }
      if (s4 == 0.0) {
        // On torg.
        return ONVERTEX;
      }
      // On edge (torg, tdest).
      return ONEDGE;
    }
    if (s3 == 0.0) {
      if (s4 == 0.0) {
        // On tapex.
        enext2self(*searchtet);
        return ONVERTEX;
      }
      // On edge (tdest, tapex).
      enextself(*searchtet);
      return ONEDGE;
    }
    if (s4 == 0.0) {
      // On edge (tapex, torg).
      enext2self(*searchtet);
      return ONEDGE;
    }
    // On face (torg, tdest, tapex).
    return ONFACE;
  }
  if (s2 == 0.0) {
    fnextself(*searchtet);
    if (s3 == 0.0) {
      if (s4 == 0.0) {
        // On toppo.
        enext2self(*searchtet);
        return ONVERTEX;
      }
      // On edge (tdest, toppo).
      enextself(*searchtet);
      return ONEDGE;
    }
    if (s4 == 0.0) {
      // On edge (toppo, torg).
      enext2self(*searchtet);
      return ONEDGE;
    }
    // On face (torg, tdest, toppo).
    return ONFACE;
  }
  if (s3 == 0.0) {
    enextfnextself(*searchtet);
    if (s4 == 0.0) {
      // On edge (tapex, toppo).
      enextself(*searchtet);
      return ONEDGE;
    }
    // On face (tdest, tapex, toppo).
    return ONFACE;
  }
  if (s4 == 0.0) {
    enext2fnextself(*searchtet);
    // On face (tapex, torg, toppo).
    return ONFACE;
  }

  // Inside tetrahedron.
  return INTETRAHEDRON;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hullwalk()    Find a tetrahedron on the hull to continue search.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt,
  triface *hulltet)
{
  list* travtetlist;
  triface travtet, neightet;
  point pa, pb, pc;
  enum locateresult loc;
  REAL ori;
  int i;

  travtetlist = new list(sizeof(triface), NULL, 256);
  travtet = *hulltet;
  infect(travtet);
  travtetlist->append(&travtet);

  loc = OUTSIDE;
  for (i = 0; i < travtetlist->len(); i++) {
    travtet = * (triface *)(* travtetlist)[i];
    // Choose the CCW-edgering in face.
    travtet.ver = 0;
    // Look for a side where pt lies below it.
    for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) {
      pa = org(travtet);
      pb = dest(travtet);
      pc = apex(travtet);
      ori = orient3d(pa, pb, pc, searchpt);
      if (ori > 0.0) break;
    }
    // Is pt above all (or coplanar with some of) the four sides?
    if (travtet.loc == 4) {
      hulltet->tet = travtet.tet;
      loc = adjustlocate(searchpt, hulltet, INTETRAHEDRON, b->epsilon);
      assert(loc != OUTSIDE);
    } else { // ori > 0.0
      // pt is below (behind) this side. We want to walk through it.
      sym(travtet, neightet);
      if (neightet.tet == dummytet) {
        // This is a hull side. Is p approximately on this side.
        loc = adjustlocate(searchpt, &travtet, OUTSIDE, b->epsilon);
      }
      if (loc == OUTSIDE) {
        // Let's collect all the neighbors for next searching.
        for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) {
          sym(travtet, neightet);
          if ((neightet.tet != dummytet) && !infected(neightet)) {
            // Neighbor exists and not visited.
            infect(neightet);
            travtetlist->append(&neightet);
          }
        } // for (travtet.loc = 0; 
      } // if (loc == OUTSIDE)
    } // if (travtet.loc == 4)
    if (loc != OUTSIDE) break;
  } // for (i = 0; i < travtetlist->len(); i++)
  
  // Uninfect traversed tets.
  for (i = 0; i < travtetlist->len(); i++) {
    travtet = * (triface *)(* travtetlist)[i];
    uninfect(travtet);
  }

  delete travtetlist;
  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locatesub()    Find a point in the surface mesh of a facet.               //
//                                                                           //
// Searching begins from the input 'searchsh', it should be a handle on the  //
// convex hull of the facet triangulation.                                   //
//                                                                           //
// If 'stopatseg' is nonzero, the search will stop if it tries to walk       //
// through a subsegment, and will return OUTSIDE.                            //
//                                                                           //
// On completion, 'searchsh' is a subface that contains 'searchpt'.          //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh'  //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies on a mesh edge.  'searchsh' is a     //
//     handle whose primary edge is the edge on which the point lies.        //
//   - Returns ONFACE if the point lies strictly within a subface.           //
//     'searchsh' is a handle on which the point lies.                       //
//   - Returns OUTSIDE if the point lies outside the triangulation.          //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// not generally work after the holes and concavities have been carved.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt,
  face* searchsh, int stopatseg, REAL epspp)
{
  face backtracksh, spinsh, checkedge;
  point forg, fdest, fapex;
  REAL orgori, destori;
  REAL ori, sign;
  int moveleft, i;

  if (searchsh->sh == dummysh) {
    searchsh->shver = 0;
    spivotself(*searchsh);
#ifdef SELF_CHECK
    assert(searchsh->sh != dummysh);
#endif
  }
  // Find the sign to simulate that abovepoint is 'above' the facet.
  adjustedgering(*searchsh, CCW);
  forg = sorg(*searchsh);
  fdest = sdest(*searchsh);
  fapex = sapex(*searchsh);
  ori = orient3d(forg, fdest, fapex, abovepoint);
  sign = ori > 0.0 ? -1 : 1;

  // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has
  //   CCW orientation with respect to searchsh in plane).  Such edge
  //   should always exist. Save it as (forg, fdest).
  for (i = 0; i < 3; i++) {
    forg = sorg(*searchsh);
    fdest = sdest(*searchsh);
    ori = orient3d(forg, fdest, abovepoint, searchpt) * sign;
    if (ori > 0.0) break;
    senextself(*searchsh);
  }
#ifdef SELF_CHECK
  assert(i < 3);
#endif
  
  while (1) {
    fapex = sapex(*searchsh);
    // Check whether the apex is the point we seek.
    if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] &&
        fapex[2] == searchpt[2]) {
      senext2self(*searchsh);
      return ONVERTEX;
    }
    // Does the point lie on the other side of the line defined by the
    //   triangle edge opposite the triangle's destination?
    destori = orient3d(forg, fapex, abovepoint, searchpt) * sign;
    if (epspp > 0.0) {
      if (iscoplanar(forg, fapex, abovepoint, searchpt, destori, epspp)) {
        destori = 0.0;
      }
    }
    // Does the point lie on the other side of the line defined by the
    //   triangle edge opposite the triangle's origin? 
    orgori = orient3d(fapex, fdest, abovepoint, searchpt) * sign;
    if (epspp > 0.0) {
      if (iscoplanar(fapex, fdest, abovepoint, searchpt, orgori, epspp)) {
        orgori = 0.0;
      }
    }
    if (destori > 0.0) {
      moveleft = 1;
    } else {
      if (orgori > 0.0) {
        moveleft = 0;
      } else {
        // The point must be on the boundary of or inside this triangle.
        if (destori == 0.0) {
          senext2self(*searchsh);
          return ONEDGE;
        } 
        if (orgori == 0.0) {
          senextself(*searchsh);
          return ONEDGE;
        }
        return ONFACE;
      }
    }
    // Move to another triangle.  Leave a trace `backtracksh' in case
    //   walking off a boundary of the triangulation.
    if (moveleft) {
      senext2(*searchsh, backtracksh);
      fdest = fapex;
    } else {
      senext(*searchsh, backtracksh);
      forg = fapex;
    }
    // Check if we meet a segment.
    sspivot(backtracksh, checkedge);
    if (checkedge.sh != dummysh) {
      if (stopatseg) {
        // The flag indicates we should not cross a segment. Stop.
        *searchsh = backtracksh;
        return OUTSIDE;
      }
      // Try to walk through a segment. We need to find a coplanar subface
      //   sharing this segment to get into.
      spinsh = backtracksh;
      do {
        spivotself(spinsh);
        if (spinsh.sh == backtracksh.sh) {
          // Turn back, no coplanar subface is found.
          break;
        }
        // Are they belong to the same facet.
        if (shellmark(spinsh) == shellmark(backtracksh)) {
          // Find a coplanar subface. Walk into it.
          *searchsh = spinsh;
          break;
        }
        // Are they (nearly) coplanar?
        ori = orient3d(forg, fdest, sapex(backtracksh), sapex(spinsh));
        if (iscoplanar(forg, fdest, sapex(backtracksh), sapex(spinsh), ori,
                       b->epsilon)) {
          // Find a coplanar subface. Walk into it.
          *searchsh = spinsh;
          break;
        }
      } while (spinsh.sh != backtracksh.sh);
    } else {
      spivot(backtracksh, *searchsh);
    }
    // Check for walking right out of the triangulation.
    if ((searchsh->sh == dummysh) || (searchsh->sh == backtracksh.sh)) {
      // Go back to the last triangle.
      *searchsh = backtracksh;
      return OUTSIDE;
    }
    // To keep the same orientation wrt abovepoint.
    if (sorg(*searchsh) != forg) sesymself(*searchsh);
#ifdef SELF_CHECK
    assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest));
#endif
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocatesub()    Adjust the precise location of a vertex.             //
//                                                                           //
// 'precise' is the precise location (returned from locatesub()) of 'searcht'//
// with respect to 'searchsh'. 'epspp' is the given relative tolerance.      //
//                                                                           //
// This routine re-evaluates the orientations of 'searchpt' with respect to  //
// the three edges of 'searchsh'. Detects the collinearities by additinal    //
// tests based on the given tolerance. If 'precise' is ONEDGE, one can save  //
// one orientation test for the current edge of 'searchsh'.                  //
//                                                                           //
// On completion, 'searchsh' is a subface contains 'searchpt'. The returned  //
// value indicates one of the following cases:                               //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh'  //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies on a mesh edge.  'searchsh' is a     //
//     handle whose primary edge is the edge on which the point lies.        //
//   - Returns ONFACE if the point lies strictly within a subface.           //
//     'searchsh' is a handle on which the point lies.                       //
//   - Returns OUTSIDE if the point lies outside 'searchsh'.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise,
                REAL epspp)
{
  point pa, pb, pc;
  bool s1, s2, s3;

  pa = sorg(*searchsh);
  pb = sdest(*searchsh);
  pc = sapex(*searchsh);

  if (precise == ONEDGE) {
    s1 = true;
  } else {
    s1 = iscollinear(pa, pb, searchpt, epspp);
  }
  s2 = iscollinear(pb, pc, searchpt, epspp);
  s3 = iscollinear(pc, pa, searchpt, epspp);
  if (s1) {
    if (s2) {
      // on vertex pb.
#ifdef SELF_CHECK
      assert(!s3);
#endif
      senextself(*searchsh);
      return ONVERTEX;
    } else if (s3) {
      // on vertex pa.
      return ONVERTEX;
    } else {
      // on edge pa->pb.
      return ONEDGE;
    }
  } else if (s2) {
    if (s3) {
      // on vertex pc.
      senext2self(*searchsh);
      return ONVERTEX;
    } else {
      // on edge pb->pc.
      senextself(*searchsh);
      return ONEDGE;
    }
  } else if (s3) {
    // on edge pc->pa.
    senext2self(*searchsh);
    return ONEDGE;
  } else {
    return precise;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locateseg()    Find a point in subsegments.                               //
//                                                                           //
// Searching begins from the input 'searchseg', it should be a subsegment of //
// the whole segment.                                                        //
//                                                                           //
// On completion, 'searchseg' is a subsegment that contains 'searchpt'.      //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchseg' //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies inside 'searchseg'.                  //
//   - Returns OUTSIDE if the point lies outside the segment.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
locateseg(point searchpt, face* searchseg)
{
  face backtraceseg;
  point pa, pb;
  REAL dx, dy, dz;
  int moveleft;
  int i;

  moveleft = 0;
  while (1) {
    searchseg->shver = 0;
    pa = sorg(*searchseg);
    pb = sdest(*searchseg);
    // Find the biggest difference in x, y, and z coordinates of a and b.
    dx = fabs(pb[0] - pa[0]);
    dy = fabs(pb[1] - pa[1]);
    dz = fabs(pb[2] - pa[2]);
    if (dx > dy) {
      if (dx > dz) {
        i = 0;
      } else {
        i = 2;
      }
    } else {
      if (dy > dz) {
        i = 1;
      } else {
        i = 2;
      }
    }
    if (pa[i] < pb[i]) {
      if (searchpt[i] < pa[i]) {
        moveleft = 1;
      } else if (searchpt[i] > pa[i]) {
        if (searchpt[i] < pb[i]) {
          return ONEDGE;
        } else if (searchpt[i] > pb[i]) {
          moveleft = 0;
        } else {
#ifdef SELF_CHECK
          assert(searchpt[i] == pb[i]);
#endif
          sesymself(*searchseg);
          return ONVERTEX;
        }
      } else {
#ifdef SELF_CHECK
        assert(searchpt[i] == pa[i]);
#endif
        return ONVERTEX;
      }
    } else if (pa[i] > pb[i]) {
      if (searchpt[i] < pb[i]) {
        moveleft = 0;
      } else if (searchpt[i] > pb[i]) {
        if (searchpt[i] < pa[i]) {
          return ONEDGE;
        } else if (searchpt[i] > pa[i]) {
          moveleft = 1;
        } else {
#ifdef SELF_CHECK
          assert(searchpt[i] == pa[i]);
#endif
          return ONVERTEX;
        }
      } else {
#ifdef SELF_CHECK
        assert(searchpt[i] == pb[i]);
#endif
        sesymself(*searchseg);
        return ONVERTEX;
      }
    }
    backtraceseg = *searchseg;
    if (moveleft) {
      senext2self(*searchseg);
    } else {
      senextself(*searchseg);
    }
    spivotself(*searchseg);
    if (searchseg->sh == dummysh) {
      *searchseg = backtraceseg;
      break;
    }
  }

  return OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocateseg()    Adjust the precise location of a vertex on segment.  //
//                                                                           //
// 'searchpt' is either inside or ouside the segment 'searchseg'. It will be //
// adjusted to on vertex if it is very close to an endpoint of 'searchseg'.  //
// 'epspp' is the given relative tolerance.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
adjustlocateseg(point searchpt, face* searchseg, enum locateresult precise,
                REAL epspp)
{
  point pa, pb;
  REAL L, d, r;

  pa = sorg(*searchseg);
  pb = sdest(*searchseg);
  L = distance(pa, pb);

  // Is searchpt approximate to pa?
  d = distance(pa, searchpt);
  r = d / L;
  if (r <= epspp) {
    return ONVERTEX;
  }
  // Is searchpt approximate to pb?
  d = distance(pb, searchpt);
  r = d / L;
  if (r <= epspp) {
    sesymself(*searchseg);
    return ONVERTEX;
  }

  return precise;
}

//
// End of point location routines
//

//
// Begin of mesh transformation routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Flip operations                                                           //
//                                                                           //
// If abc is a hull face, it is unflipable, and is locally Delaunay.  In the //
// following, we assume abc is an interior face, and the other tetrahedron   //
// adjoining at abc is bace.                                                 //
//                                                                           //
// If the convex hull CH of the set {a, b, c, d, e} only has four vertices,  //
// i.e., one vertex lies inside CH, then abc is unflipable, and is locally   //
// Delaunay. If CH is the vertex set itself, we have the following cases to  //
// determine whether abc is flipable or not.                                 //
//                                                                           //
// If no four points of {a, b, c, d, e} are coplanar, a 2-to-3 flip can be   //
// applied to abc if the edge de crosses the triangle abc; a 3-to-2 flip can //
// be applied to abc if ab crosses cde, and abde exists, otherwise, face abc //
// is unflipable, i.e., the tetrahedron abde is not present.                 //
//                                                                           //
// If four points of {a, b, c, d, e} are coplanar (two faces are coplanar).  //
// Assume faces abd and abe are coplanar (it is impossible be abc). If a, b, //
// d, e form a non-convex quadrilateral, then abc is unflipable, furthermore,//
// it is locally Delaunay.  Assume they are convex quadrilateral, if abd and //
// abe are hull faces, a 2-to-2 flip can be applied to abc;  if abd and abe  //
// are interior faces,  assume two tetrahedra adjoining abd and abe at the   //
// opposite sides are abdg and abef, respectively.  If g = f, a 4-to-4 flip  //
// can be applied to abc, otherwise, abc is unflipable.                      //
//                                                                           //
// There are other cases which can cause abc unflipable. If abc is a subface,//
// a 2-to-3 flip is forbidden;  if ab is a subsegment, flips 3-to-2, 2-to-2, //
// and 4-to-4 are forbidden.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// categorizeface()    Determine the flip type of a given face.              //
//                                                                           //
// On input, 'horiz' represents the face abc we want to flip (imagine it is  //
// parallel to the horizon).  Let the tet above it be abcd.                  //
//                                                                           //
// This routine determines the suitable type of flip operation for 'horiz'.  //
//   - Returns T23 if a 2-to-3 flip is applicable. 'horiz' is same as input. //
//   - Returns T32 if a 3-to-2 flip is applicable. 'horiz' returns the edge  //
//     of abc which is the flipable.                                         //
//   - Returns T22 if a 2-to-2 or 4-to-4 flip is applicable. 'horiz' returns //
//     the edge of abc which is flipable.                                    //
//   - Returns N32 indicates it is unflipable due to the absence of a tet.   //
//     'horize' returns the unflipable edge.                                 //
//   - Returns N40 indicates it is unflipable and is locally Delaunay.       //
//   - Returns FORBIDDENFACE indicates abc is a subface.                     //
//   - Returns FORBIDDENEDGE indicates the flipable edge of abc is a segment.//
//     'horize' returns the flipable edge.                                   //
//                                                                           //
// Given a face abc, with two adjoining tetrahedra abcd and bace.  If abc is //
// flipable, i.e., T23, T32, T22 or T44, its flip type can be determined by  //
// doing five orientation tests: two tests for determining that d, e lie on  //
// the different sides of abc, three tests for determining if the edge de    //
// intersects the face abc.  However, if we use the neighbor information of  //
// the mesh data structure, we can reduce the five orientation tests to at   //
// most three tests, that is, the two tests for determining whether d and e  //
// lie on the different sides of abc can be saved.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::fliptype tetgenmesh::categorizeface(triface& horiz)
{
  triface symhoriz, casing;
  face checksh, checkseg;
  face cassh1, cassh2;
  point pa, pb, pc, pd, pe, pf, pg;
  point abdoppo, bcdoppo, cadoppo;
  REAL ori1, ori2, ori3;
  int adjtet;

  sym(horiz, symhoriz);
  if (symhoriz.tet == dummytet) {
    // A hull face is unflipable and locally Delaunay.
    return N40;
  }
  
  adjustedgering(horiz, CCW);
  findedge(&symhoriz, dest(horiz), org(horiz));
  pa = org(horiz);
  pb = dest(horiz);
  pc = apex(horiz);
  pd = oppo(horiz);
  pe = oppo(symhoriz);

  // Find the number of adjacent tetrahedra of abc, which have d, e, and one
  //   of corners of abc as their corners. This number can be 0, 1 and 2.
  abdoppo = bcdoppo = cadoppo = (point) NULL;
  adjtet = 0;
  fnext(horiz, casing); // at edge 'ab'.
  symself(casing);
  if (casing.tet != dummytet) {
    abdoppo = oppo(casing);
    if (abdoppo == pe) adjtet++;
  }
  enextfnext(horiz, casing); // at edge 'bc'.
  symself(casing);
  if (casing.tet != dummytet) {
    bcdoppo = oppo(casing);
    if (bcdoppo == pe) adjtet++;
  }
  enext2fnext(horiz, casing); // at edge 'ca'.
  symself(casing);
  if (casing.tet != dummytet) {
    cadoppo = oppo(casing);
    if (cadoppo == pe) adjtet++;
  }
  
  if (adjtet == 0) {
    // No adjacent tetrahedron. Types T23, T22 and T44 are possible. 
    ori1 = orient3d(pa, pb, pd, pe);
    if (checksubfaces && ori1 != 0.0) {
      // Check if abd and abe are both boundary faces?
      fnext(horiz, casing);
      tspivot(casing, cassh1);
      fnext(symhoriz, casing);
      tspivot(casing, cassh2);
      if ((cassh1.sh != dummysh) && (cassh2.sh != dummysh)) {
        // abd and abe are both boundary faces. Check if ab is a segment.
        findedge(&cassh1, pa, pb);
        sspivot(cassh1, checkseg);
        if (checkseg.sh == dummysh) {
          // ab is not a segment - abd and abe belong to the same facet.
          //   The four points are forced to be coplanar.
          ori1 = 0.0;
        } else {
          // ab is a segment - abd and abe belong to two different facets.
          //   In principle, a, b, c and d can form a tetrahedron (since
          //   ori1 != 0.0).  However, we should avoid to create a very
          //   flat one which may form a sequence of extremely badly-shaped
          //   or even wrong orientational tets. Test with a larger epsilon.
          if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon * 1e+2)) ori1 = 0.0;
        }
      } else {
        // abd and abe are not both boundary faces. Check if abd and bae
        //   are approximately coplanar with respect to the epsilon.
        if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon)) ori1 = 0.0;
      }
    }
    if (ori1 < 0.0) {
      // e lies above abd, unflipable, tet abde is not present.
#ifdef SELF_CHECK
      if (!nonconvex) {
        // abd and abe should not be hull faces, check it.
        fnext(horiz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
        fnext(symhoriz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
      }
#endif
      if (checksubfaces) {
        // The nonconvexbility may be casued by existing an subsegment.
        tsspivot(&horiz, &checkseg);
        if (checkseg.sh != dummysh) {
          return FORBIDDENEDGE;
        }
      }
      return N32;
    }
    ori2 = orient3d(pb, pc, pd, pe);
    if (checksubfaces && ori2 != 0.0) {
      // Check if bcd and cbe are both boundary faces.
      enextfnext(horiz, casing);
      tspivot(casing, cassh1);
      enext2fnext(symhoriz, casing);
      tspivot(casing, cassh2);
      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
        // bcd and cbe are both boundary faces. Check if bc is a segment.
        findedge(&cassh1, pb, pc);
        sspivot(cassh1, checkseg);
        if (checkseg.sh == dummysh) {
          // bc is not a segment - bcd and cbe belong to the same facet.
          //   The four points are forced to be coplanar.
          ori2 = 0.0;
        } else {
          if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon * 1e+2)) ori2 = 0.0;
        } 
      } else {
        //  bcd and cbe are not both boundary faces. Check if bcd and cbe
        //   are approximately coplanar with respect to the epsilon.
        if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon)) ori2 = 0.0;
      }
    }
    if (ori2 < 0.0) {
      // e lies above bcd, unflipable, tet bcde is not present.
#ifdef SELF_CHECK
      if (!nonconvex) {
        // bcd and cbe should not be hull faces, check it.
        enextfnext(horiz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
        enext2fnext(symhoriz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
      }
#endif
      enextself(horiz);
      if (checksubfaces) {
        // The nonconvexbility may be casued by existing an subsegment.
        tsspivot(&horiz, &checkseg);
        if (checkseg.sh != dummysh) {
          return FORBIDDENEDGE;
        }
      }
      return N32;
    } 
    ori3 = orient3d(pc, pa, pd, pe);
    if (checksubfaces && ori3 != 0.0) {
      // Check if cad and ace are both boundary faces.
      enext2fnext(horiz, casing);
      tspivot(casing, cassh1);
      enextfnext(symhoriz, casing);
      tspivot(casing, cassh2);
      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
        // cad and ace are both boundary faces. Check if ca is a segment.
        findedge(&cassh1, pc, pa);
        sspivot(cassh1, checkseg);
        if (checkseg.sh == dummysh) {
          // ca is not a segment - cad and ace belong to the same facet.
          //   The four points are forced to be coplanar.
          ori3 = 0.0;
        } else {
          // ca is a segment - cad and ace belong to two different facets.
          //   In principle, c, a, d and e can form a tetrahedron (since
          //   ori3 != 0.0). Use a larger eps to test if they're coplanar.
          if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon * 1e+2)) ori3 = 0.0;
        } 
      } else {
        // cad and ace are not both boundary faces. Check if cad and ace
        //   are approximately coplanar with respect to the epsilon.
        if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon)) ori3 = 0.0;
      }
    }
    if (ori3 < 0.0) {
      // e lies above cad, unflipable, tet cade is not present.
#ifdef SELF_CHECK
      if (!nonconvex) {
        // cad and ace should not be hull faces, check it.
        enext2fnext(horiz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
        enextfnext(symhoriz, casing);
        symself(casing);
        assert(casing.tet != dummytet);
      }
#endif
      enext2self(horiz);
      if (checksubfaces) {
        // The nonconvexbility may be casued by existing an subsegment.
        tsspivot(&horiz, &checkseg);
        if (checkseg.sh != dummysh) {
          return FORBIDDENEDGE;
        }
      }
      return N32;
    }
    if (ori1 == 0.0) {
      // e is coplanar with abd.
      if (ori2 * ori3 == 0.0) {
        // only one zero is possible.
        // assert(!(ori2 == 0.0 && ori3 == 0.0));
        // Three points (d, e, and a or b) are collinear, abc is unflipable
        //   and locally Delaunay.
        return N40;
      }
    } else if (ori2 == 0.0) {
      // e is coplanar with bcd.
      if (ori1 * ori3 == 0.0) {
        // only one zero is possible.
        // assert(!(ori1 == 0.0 && ori3 == 0.0));
        // Three points (d, e, and b or c) are collinear, abc is unflipable
        //   and locally Delaunay.
        return N40;
      }
      // Adjust 'horiz' and 'symhoriz' be the edge bc.
      enextself(horiz);
      enext2self(symhoriz);
    } else if (ori3 == 0.0) {
      // e is coplanar with cad.
      if (ori1 * ori2 == 0.0) {
        // only one zero is possible.
        // assert(!(ori1 == 0.0 && ori2 == 0.0));
        // Three points (d, e, and c or a) are collinear, abc is unflipable
        //   and locally Delaunay.
        return N40;
      }
      // Adjust 'horiz' and 'symhoriz' be the edge ca.
      enext2self(horiz);
      enextself(symhoriz);
    } else {
      // e lies below all three faces, flipable.
      if (checksubfaces) {
        tspivot(horiz, checksh);
        if (checksh.sh != dummysh) {
          // To flip a subface is forbidden.
          return FORBIDDENFACE;
        }
      }
      return T23;
    }
    // Four points are coplanar, T22 or T44 is possible.
    if (checksubfaces) {
      tsspivot(&horiz, &checkseg);
      if (checkseg.sh != dummysh) {
        // To flip a subsegment is forbidden.
        return FORBIDDENEDGE;
      }
      tspivot(horiz, checksh);
      if (checksh.sh != dummysh) {
        // To flip a subface is forbidden.
        return FORBIDDENFACE;
      }
    }
    // Assume the four coplanar points are a, b, d, e, abd and abe are two
    //   coplanar faces. If both abd and abe are hull faces, flipable(T22).
    //   If they are interior faces, get the opposite tetrahedra abdf and
    //   abeg, if f = g, flipable (T44). Otherwise, unflipable.
    pf = pg = (point) NULL;
    fnext(horiz, casing);
    symself(casing);
    if (casing.tet != dummytet) {
      pf = oppo(casing);
    }
    fnext(symhoriz, casing);
    symself(casing);
    if (casing.tet != dummytet) {
      pg = oppo(casing);
    }
    if (pf == pg) {
      // Either T22 (pf == pg == NULL) or T44 (pf and pg) is possible.
      if (checksubfaces) {
        // Retreat the corner points a, b, and c.
        pa = org(horiz);
        pb = dest(horiz);
        pc = apex(horiz);
        // Be careful not to create an inverted tetrahedron. Check the case.
        ori1 = orient3d(pc, pd, pe, pa);
        if (ori1 <= 0) return N40;
        ori1 = orient3d(pd, pc, pe, pb);
        if (ori1 <= 0) return N40;
        if (pf != (point) NULL) {
          ori1 = orient3d(pd, pf, pe, pa);
          if (ori1 <= 0) return N40;
          ori1 = orient3d(pf, pd, pe, pb);
          if (ori1 <= 0) return N40;
        }
      }
      if (pf == (point) NULL) {
        // abd and abe are hull faces, flipable.
        return T22;
      } else {
        // abd and abe are interior faces, flipable.
#ifdef SELF_CHECK
        assert(pf != (point) NULL);
#endif
        return T44;
      }
    } else {
      // ab has more than four faces around it, unflipable.
      return N32;
    }
  } else if (adjtet == 1) {
    // One of its three edges is locally non-convex. Type T32 is possible.
    // Adjust current configuration so that edge ab is non-convex.
    if (bcdoppo == pe) {
      // Edge bc is non-convex. Adjust 'horiz' and 'symhoriz' be edge bc.
      enextself(horiz);
      enext2self(symhoriz);
      pa = org(horiz);
      pb = dest(horiz);
      pc = apex(horiz);
    } else if (cadoppo == pe) {
      // Edge ca is non-convex. Adjust 'horiz' and 'symhoriz' be edge ca.
      enext2self(horiz);
      enextself(symhoriz);
      pa = org(horiz);
      pb = dest(horiz);
      pc = apex(horiz);
    } else {
      // Edge ab is non-convex.
#ifdef SELF_CHECK
      assert(abdoppo == pe);
#endif
    } // Now ab is the non-convex edge.
    // In order to be flipable, ab should cross face cde. Check it.
    ori1 = orient3d(pc, pd, pe, pa);
    if (checksubfaces && ori1 != 0.0) {
      // Check if cad and ace are both boundary faces.
      enext2fnext(horiz, casing);
      tspivot(casing, cassh1);
      enextfnext(symhoriz, casing);
      tspivot(casing, cassh2);
      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
        // cad and ace are both boundary faces. Check if ca is a segment.
        findedge(&cassh1, pc, pa);
        sspivot(cassh1, checkseg);
        if (checkseg.sh == dummysh) {
          // ca is not a segment. cad and ace belong to the same facet.
          //   The four points are forced to be coplanar.
          ori1 = 0.0;
        } else {
          // ca is a segment. cad and ace belong to different facets.
          //   In principle, c, d, e, and a can form a tetrahedron (since
          //   ori1 != 0.0).  However, we should avoid to create a very
          //   flat tet. Use a larger epsilon to test if they're coplanar.
          if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon * 1e+2)) ori1 = 0.0;
        }
      } else {
        // Check if c, d, e, and a are approximately coplanar.
        if (iscoplanar(pc, pd, pe, pa, ori1, b->epsilon)) ori1 = 0.0;
      }
    }
    if (ori1 <= 0.0) {
      // a lies above or is coplanar cde, abc is locally Delaunay.
      return N40;
    }
    ori2 = orient3d(pd, pc, pe, pb);
    if (checksubfaces && ori2 != 0.0) {
      // Check if bcd and cbe are both boundary faces.
      enextfnext(horiz, casing);
      tspivot(casing, cassh1);
      enext2fnext(symhoriz, casing);
      tspivot(casing, cassh2);
      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
        // bcd and cbe are both boundary faces. Check if bc is a segment.
        findedge(&cassh1, pb, pc);
        sspivot(cassh1, checkseg);
        if (checkseg.sh == dummysh) {
          // bc is not a segment. bcd and cbe belong to the same facet.
          //   The four points are forced to be coplanar.
          ori2 = 0.0;
        } else {
          // bc is a segment. bcd and cbe belong to different facets.
          //   In principle, d, c, e, and b can form a tetrahedron (since
          //   ori2 != 0.0).  However, we should avoid to create a very
          //   flat tet. Use a larger epsilon to test if they're coplanar.
          if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon * 1e+2)) ori2 = 0.0;
        }
      } else {
        // Check if d, c, e, and b are approximately coplanar.
        if (iscoplanar(pd, pc, pe, pb, ori2, b->epsilon)) ori2 = 0.0;
      }
    }
    if (ori2 <= 0.0) {
      // b lies above dce, unflipable, and abc is locally Delaunay.
      return N40;
    }
    // Edge ab crosses face cde properly.
    if (checksubfaces) {
      // If abc is subface, then ab must be a subsegment (because abde is
      //   a tetrahedron and ab crosses cde properly). 
      tsspivot(&horiz, &checkseg);
      if (checkseg.sh != dummysh) {
        // To flip a subsegment is forbidden.
        return FORBIDDENEDGE;
      }
      // Both abd and bae should not be subfaces (because they're not
      //   coplanar and ab is not a subsegment). However, they may be
      //   subfaces and belong to a facet (created during facet recovery),
      //   that is, abde is an invalid tetrahedron. Find this case out.
      fnext(horiz, casing);
      tspivot(casing, cassh1);
      fnext(symhoriz, casing);
      tspivot(casing, cassh2); 
      if (cassh1.sh != dummysh || cassh2.sh != dummysh) {
        if (!b->quiet) {
          // Unfortunately, they're subfaces. Corrections need be done here.
          printf("Warning:  A tetrahedron spans two subfaces of a facet.\n");
        }
        // Temporarily, let it be there.
        return N32;
      }
    }
    return T32;
  } else {
    // The convex hull of {a, b, c, d, e} has only four vertices, abc is
    //   unflipable, furthermore, it is locally Delaunay.
    return N40;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueueflipface(), enqueueflipedge()    Queue a face (or an edge).        //
//                                                                           //
// The face (or edge) may be non-locally Delaunay. It is queued for process- //
// ing in flip() (or flipsub()). The vertices of the face (edge) are stored  //
// seperatly to ensure the face (or edge) is still the same one when we save //
// it since other flips will cause this face (or edge) be changed or dead.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue)
{
  badface *queface;
  triface symface;

  sym(checkface, symface);
  if (symface.tet != dummytet) {
    queface = (badface *) flipqueue->push((void *) NULL);
    queface->tt = checkface;
    queface->foppo = oppo(symface);
  }
}

void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue)
{
  badface *queface;

  queface = (badface *) flipqueue->push((void *) NULL);
  queface->ss = checkedge;
  queface->forg = sorg(checkedge);
  queface->fdest = sdest(checkedge);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip23()    Perform a 2-to-3 flip.                                        //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is abc, //
// the two tetrahedra sharing abc are abcd, bace. abc is not a subface.      //
//                                                                           //
// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra  //
// edab, edbc, and edca.  As a result, face abc has been removed and three   //
// new faces eda, edb and edc have been created.                             //
//                                                                           //
// On completion, 'flipface' returns edab.  If 'flipqueue' is not NULL, all  //
// possibly non-Delaunay faces are added into it.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip23(triface* flipface, queue* flipqueue)
{
  triface abcd, bace;                                  // Old configuration.
  triface oldabd, oldbcd, oldcad;
  triface abdcasing, bcdcasing, cadcasing;
  triface oldbae, oldcbe, oldace;
  triface baecasing, cbecasing, acecasing;
  triface worktet;
  face abdsh, bcdsh, cadsh;                   // The six subfaces on the CH.
  face baesh, cbesh, acesh;
  face abseg, bcseg, caseg;                      // The nine segs on the CH.
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  triface edab, edbc, edca;                            // New configuration.
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  int i;

  abcd = *flipface;
  adjustedgering(abcd, CCW); // abcd represents edge ab.
  pa = org(abcd);
  pb = dest(abcd);
  pc = apex(abcd);
  pd = oppo(abcd);
  // sym(abcd, bace);
  // findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba.
  sym(abcd, bace);
  bace.ver = 0; // CCW.
  for (i = 0; (i < 3) && (org(bace) != pb); i++) {
    enextself(bace);
  }
  pe = oppo(bace);

  if (b->verbose > 2) {
    printf("    Do T23 on face (%d, %d, %d) %d, %d.\n", pointmark(pa),
           pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe));
  }
  flip23s++;

  // Storing the old configuration outside the convex hull.
  fnext(abcd, oldabd);
  enextfnext(abcd, oldbcd);
  enext2fnext(abcd, oldcad);
  fnext(bace, oldbae);
  enext2fnext(bace, oldcbe);
  enextfnext(bace, oldace);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  sym(oldbae, baecasing);
  sym(oldcbe, cbecasing);
  sym(oldace, acecasing);
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    tspivot(oldbcd, bcdsh);
    tspivot(oldcad, cadsh);
    tspivot(oldbae, baesh);
    tspivot(oldcbe, cbesh);
    tspivot(oldace, acesh);
  } else if (checksubsegs) {
    tsspivot1(abcd, abseg);
    enext(abcd, worktet);
    tsspivot1(worktet, bcseg);
    enext2(abcd, worktet);
    tsspivot1(worktet, caseg);
    enext2(oldabd, worktet);
    tsspivot1(worktet, adseg);
    enext2(oldbcd, worktet);
    tsspivot1(worktet, bdseg);
    enext2(oldcad, worktet);
    tsspivot1(worktet, cdseg);
    enext(oldbae, worktet);
    tsspivot1(worktet, aeseg);
    enext(oldcbe, worktet);
    tsspivot1(worktet, beseg);
    enext(oldace, worktet);
    tsspivot1(worktet, ceseg);
  }

  // Creating the new configuration inside the convex hull.
  edab.tet = abcd.tet; // Update abcd to be edab.
  setorg (edab, pe);
  setdest(edab, pd);
  setapex(edab, pa);
  setoppo(edab, pb);
  edbc.tet = bace.tet; // Update bace to be edbc.
  setorg (edbc, pe);
  setdest(edbc, pd);
  setapex(edbc, pb);
  setoppo(edbc, pc);
  maketetrahedron(&edca); // Create edca.
  setorg (edca, pe);
  setdest(edca, pd);
  setapex(edca, pc);
  setoppo(edca, pa);
  // Set the element attributes of the new tetrahedron 'edca'.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abcd.tet, i);
    setelemattribute(edca.tet, i, attrib);
  }
  // Set the volume constraint of the new tetrahedron 'edca' if the -ra
  //   switches are not used together. In -ra case, the various volume
  //   constraints can be spreaded very far.
  if (b->varvolume && !b->refine) {
    volume = volumebound(abcd.tet);
    setvolumebound(edca.tet, volume);
  }

  // Clear old bonds in edab(was abcd) and edbc(was bace).
  for (i = 0; i < 4; i ++) {
    edab.tet[i] = (tetrahedron) dummytet;
  }
  for (i = 0; i < 4; i ++) {
    edbc.tet[i] = (tetrahedron) dummytet;
  }
  // Bond the faces inside the convex hull.
  edab.loc = 0;
  edca.loc = 1;
  bond(edab, edca);
  edab.loc = 1;
  edbc.loc = 0;
  bond(edab, edbc);
  edbc.loc = 1;
  edca.loc = 0;
  bond(edbc, edca);
  // Bond the faces on the convex hull.
  edab.loc = 2;
  bond(edab, abdcasing);
  edab.loc = 3;
  bond(edab, baecasing);
  edbc.loc = 2;
  bond(edbc, bcdcasing);
  edbc.loc = 3;
  bond(edbc, cbecasing);
  edca.loc = 2;
  bond(edca, cadcasing);
  edca.loc = 3;
  bond(edca, acecasing);
  // There may exist subfaces that need to be bonded to new configuarton.
  if (checksubfaces) {
    // Clear old flags in edab(was abcd) and edbc(was bace).
    for (i = 0; i < 4; i ++) {
      edab.loc = i;
      tsdissolve(edab);
      edbc.loc = i;
      tsdissolve(edbc);
    }
    if (abdsh.sh != dummysh) {
      edab.loc = 2; 
      tsbond(edab, abdsh);
    }
    if (baesh.sh != dummysh) {
      edab.loc = 3; 
      tsbond(edab, baesh);
    }
    if (bcdsh.sh != dummysh) {
      edbc.loc = 2; 
      tsbond(edbc, bcdsh);
    }
    if (cbesh.sh != dummysh) {
      edbc.loc = 3; 
      tsbond(edbc, cbesh);
    }
    if (cadsh.sh != dummysh) {
      edca.loc = 2; 
      tsbond(edca, cadsh);
    }
    if (acesh.sh != dummysh) {
      edca.loc = 3; 
      tsbond(edca, acesh);
    }
  } else if (checksubsegs) {
    for (i = 0; i < 6; i++) {
      edab.tet[8 + i] = (tetrahedron) dummysh;
    }
    for (i = 0; i < 6; i++) {
      edbc.tet[8 + i] = (tetrahedron) dummysh;
    }
    edab.loc = edab.ver = 0;
    edbc.loc = edab.ver = 0;
    edca.loc = edab.ver = 0;
    // Operate in tet edab (5 edges).
    enext(edab, worktet);
    tssbond1(worktet, adseg);
    enext2(edab, worktet);
    tssbond1(worktet, aeseg);
    fnext(edab, worktet);
    enextself(worktet);
    tssbond1(worktet, bdseg);
    enextself(worktet);
    tssbond1(worktet, beseg);
    enextfnext(edab, worktet);
    enextself(worktet);
    tssbond1(worktet, abseg);
    // Operate in tet edbc (5 edges)
    enext(edbc, worktet);
    tssbond1(worktet, bdseg);
    enext2(edbc, worktet);
    tssbond1(worktet, beseg);
    fnext(edbc, worktet);
    enextself(worktet);
    tssbond1(worktet, cdseg);
    enextself(worktet);
    tssbond1(worktet, ceseg);
    enextfnext(edbc, worktet);
    enextself(worktet);
    tssbond1(worktet, bcseg);
    // Operate in tet edca (5 edges)
    enext(edca, worktet);
    tssbond1(worktet, cdseg);
    enext2(edca, worktet);
    tssbond1(worktet, ceseg);
    fnext(edca, worktet);
    enextself(worktet);
    tssbond1(worktet, adseg);
    enextself(worktet);
    tssbond1(worktet, aeseg);
    enextfnext(edca, worktet);
    enextself(worktet);
    tssbond1(worktet, caseg);
  }

  edab.loc = 0;
  edbc.loc = 0;
  edca.loc = 0;
  if (b->verbose > 3) {
    printf("    Updating edab ");
    printtet(&edab);
    printf("    Updating edbc ");
    printtet(&edbc);
    printf("    Creating edca ");
    printtet(&edca);
  }

  if (flipqueue != (queue *) NULL) {
    enextfnext(edab, abdcasing);
    enqueueflipface(abdcasing, flipqueue);
    enext2fnext(edab, baecasing);
    enqueueflipface(baecasing, flipqueue);
    enextfnext(edbc, bcdcasing);
    enqueueflipface(bcdcasing, flipqueue);
    enext2fnext(edbc, cbecasing);
    enqueueflipface(cbecasing, flipqueue);
    enextfnext(edca, cadcasing);
    enqueueflipface(cadcasing, flipqueue);
    enext2fnext(edca, acecasing);
    enqueueflipface(acecasing, flipqueue);  
  }

  // Save a live handle in 'recenttet'.
  recenttet = edbc;
  // Set the return handle be edab.
  *flipface = edab;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip32()    Perform a 3-to-2 flip.                                        //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is eda, //
// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,//
// edbc, and edca.  ed is not a subsegment.                                  //
//                                                                           //
// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into //
// another two tetrahedra abcd and bace.  As a result, the edge ed has been  //
// removed and the face abc has been created.                                //
//                                                                           //
// On completion, 'flipface' returns abcd.  If 'flipqueue' is not NULL, all  //
// possibly non-Delaunay faces are added into it.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip32(triface* flipface, queue* flipqueue)
{
  triface edab, edbc, edca;                            // Old configuration.
  triface oldabd, oldbcd, oldcad;
  triface abdcasing, bcdcasing, cadcasing;
  triface oldbae, oldcbe, oldace;
  triface baecasing, cbecasing, acecasing;
  triface worktet;
  face abdsh, bcdsh, cadsh;
  face baesh, cbesh, acesh;
  face abseg, bcseg, caseg;                      // The nine segs on the CH.
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  triface abcd, bace;                                  // New configuration.
  point pa, pb, pc, pd, pe;
  int i;

  edab = *flipface;
  adjustedgering(edab, CCW);
  pa = apex(edab);
  pb = oppo(edab);
  pd = dest(edab);
  pe = org(edab);
  fnext(edab, edbc);
  symself(edbc);
  edbc.ver = 0;
  for (i = 0; (i < 3) && (org(edbc) != pe); i++) {
    enextself(edbc);
  }
  pc = oppo(edbc);
  fnext(edbc, edca);
  symself(edca);
  edca.ver = 0;
  for (i = 0; (i < 3) && (org(edca) != pe); i++) {
    enextself(edca);
  }

  if (b->verbose > 2) {
    printf("    Do T32 on edge (%d, %d) %d, %d, %d.\n", pointmark(pe),
           pointmark(pd), pointmark(pa), pointmark(pb), pointmark(pc));
  }
  flip32s++;

  // Storing the old configuration outside the convex hull.
  enextfnext(edab, oldabd);
  enext2fnext(edab, oldbae);
  enextfnext(edbc, oldbcd);
  enext2fnext(edbc, oldcbe);
  enextfnext(edca, oldcad);
  enext2fnext(edca, oldace);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  sym(oldbae, baecasing);
  sym(oldcbe, cbecasing);
  sym(oldace, acecasing);
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    tspivot(oldbcd, bcdsh);
    tspivot(oldcad, cadsh);
    tspivot(oldbae, baesh);
    tspivot(oldcbe, cbesh);
    tspivot(oldace, acesh);
  } else if (checksubsegs) {
    enext(edab, worktet);
    tsspivot1(worktet, adseg);
    enext2(edab, worktet);
    tsspivot1(worktet, aeseg);
    enext(edbc, worktet);
    tsspivot1(worktet, bdseg);
    enext2(edbc, worktet);
    tsspivot1(worktet, beseg);
    enext(edca, worktet);
    tsspivot1(worktet, cdseg);
    enext2(edca, worktet);
    tsspivot1(worktet, ceseg);
    enextfnext(edab, worktet);
    enextself(worktet);
    tsspivot1(worktet, abseg);
    enextfnext(edbc, worktet);
    enextself(worktet);
    tsspivot1(worktet, bcseg);
    enextfnext(edca, worktet);
    enextself(worktet);
    tsspivot1(worktet, caseg);
  }

  // Creating the new configuration inside the convex hull.
  abcd.tet = edab.tet; // Update edab to be abcd.
  setorg (abcd, pa);
  setdest(abcd, pb);
  setapex(abcd, pc);
  setoppo(abcd, pd);
  bace.tet = edbc.tet; // Update edbc to be bace.
  setorg (bace, pb);
  setdest(bace, pa);
  setapex(bace, pc);
  setoppo(bace, pe);
  // Dealloc a redundant tetrahedron (edca).
  tetrahedrondealloc(edca.tet); 

  // Clear the old bonds in abcd (was edab) and bace (was edbc).
  for (i = 0; i < 4; i ++) {
    abcd.tet[i] = (tetrahedron) dummytet;
  }
  for (i = 0; i < 4; i ++) {
    bace.tet[i] = (tetrahedron) dummytet;
  }
  // Bond the inside face of the convex hull.
  abcd.loc = 0;
  bace.loc = 0;
  bond(abcd, bace);
  // Bond the outside faces of the convex hull.
  abcd.loc = 1;
  bond(abcd, abdcasing);
  abcd.loc = 2;
  bond(abcd, bcdcasing);
  abcd.loc = 3;
  bond(abcd, cadcasing);
  bace.loc = 1;
  bond(bace, baecasing);
  bace.loc = 3;
  bond(bace, cbecasing);
  bace.loc = 2;
  bond(bace, acecasing);
  if (checksubfaces) {
    // Clear old bonds in abcd(was edab) and bace(was edbc).
    for (i = 0; i < 4; i ++) {
      abcd.tet[8 + i] = (tetrahedron) dummysh;
    }
    for (i = 0; i < 4; i ++) {
      bace.tet[8 + i] = (tetrahedron) dummysh;
    }
    if (abdsh.sh != dummysh) {
      abcd.loc = 1;
      tsbond(abcd, abdsh);
    }
    if (bcdsh.sh != dummysh) {
      abcd.loc = 2;
      tsbond(abcd, bcdsh);
    }
    if (cadsh.sh != dummysh) {
      abcd.loc = 3;
      tsbond(abcd, cadsh);
    }
    if (baesh.sh != dummysh) {
      bace.loc = 1;
      tsbond(bace, baesh);
    }
    if (cbesh.sh != dummysh) {
      bace.loc = 3;
      tsbond(bace, cbesh);
    }
    if (acesh.sh != dummysh) {
      bace.loc = 2;
      tsbond(bace, acesh);
    }
  } else if (checksubsegs) {
    for (i = 0; i < 6; i++) {
      abcd.tet[8 + i] = (tetrahedron) dummysh;
    }
    for (i = 0; i < 6; i++) {
      bace.tet[8 + i] = (tetrahedron) dummysh;
    }
    abcd.loc = abcd.ver = 0;
    bace.loc = bace.ver = 0;
    tssbond1(abcd, abseg);     // 1
    enext(abcd, worktet);
    tssbond1(worktet, bcseg);  // 2
    enext2(abcd, worktet);
    tssbond1(worktet, caseg);  // 3
    fnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, adseg);  // 4
    enextfnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, bdseg);  // 5
    enext2fnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, cdseg);  // 6
    tssbond1(bace, abseg);
    enext2(bace, worktet);
    tssbond1(worktet, bcseg);
    enext(bace, worktet);
    tssbond1(worktet, caseg);
    fnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, aeseg);  // 7
    enext2fnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, beseg);  // 8
    enextfnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, ceseg);  // 9
  }

  abcd.loc = 0;
  bace.loc = 0;
  if (b->verbose > 3) {
    printf("    Updating abcd ");
    printtet(&abcd);
    printf("    Updating bace ");
    printtet(&bace);
    printf("    Deleting edca ");
    // printtet(&edca);
  }

  if (flipqueue != (queue *) NULL) { 
    fnext(abcd, abdcasing);
    enqueueflipface(abdcasing, flipqueue);
    fnext(bace, baecasing);
    enqueueflipface(baecasing, flipqueue);
    enextfnext(abcd, bcdcasing);
    enqueueflipface(bcdcasing, flipqueue);
    enextfnext(bace, cbecasing);
    enqueueflipface(cbecasing, flipqueue);
    enext2fnext(abcd, cadcasing);
    enqueueflipface(cadcasing, flipqueue);
    enext2fnext(bace, acecasing);
    enqueueflipface(acecasing, flipqueue);  
  }

  // Save a live handle in 'recenttet'.
  recenttet = abcd;
  // Set the return handle be abcd.
  *flipface = abcd;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip22()    Perform a 2-to-2 (or 4-to-4) flip.                            //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is abe, //
// ab is the flipable edge, the two tetrahedra sharing abe are abce and bade,//
// hence a, b, c and d are coplanar. If abc, bad are interior faces, the two //
// tetrahedra opposite to e are bacf and abdf.  ab is not a subsegment.      //
//                                                                           //
// A 2-to-2 flip is to change two tetrahedra abce and bade into another two  //
// tetrahedra dcae and cdbe. If bacf and abdf exist, they're changed to cdaf //
// and dcbf, thus a 4-to-4 flip.  As a result, two or four tetrahedra have   //
// rotated counterclockwise (using right-hand rule with thumb points to e):  //
// abce->dcae, bade->cdbe, and bacf->cdaf, abdf->dcbf.                       //
//                                                                           //
// If abc and bad are subfaces, a 2-to-2 flip is performed simultaneously by //
// calling routine flip22sub(), hence abc->dca, bad->cdb.  The edge rings of //
// the flipped subfaces dca and cdb have the same orientation as abc and bad.//
// Hence, they have the same orientation as other subfaces of the facet with //
// respect to the lift point of this facet.                                  //
//                                                                           //
// On completion, 'flipface' holds edge dc of tetrahedron dcae. 'flipqueue'  //
// contains all possibly non-Delaunay faces if it is not NULL.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip22(triface* flipface, queue* flipqueue)
{
  triface abce, bade;
  triface oldbce, oldcae, oldade, olddbe;
  triface bcecasing, caecasing, adecasing, dbecasing;
  face bcesh, caesh, adesh, dbesh;
  triface bacf, abdf;
  triface oldacf, oldcbf, oldbdf, olddaf;
  triface acfcasing, cbfcasing, bdfcasing, dafcasing;
  triface worktet;
  face acfsh, cbfsh, bdfsh, dafsh;
  face abc, bad;
  face adseg, dbseg, bcseg, caseg;  // Coplanar segs.
  face aeseg, deseg, beseg, ceseg;  // Above segs.
  face afseg, dfseg, bfseg, cfseg;  // Below segs.
  point pa, pb, pc, pd, pe, pf;
  int mirrorflag, i;

  adjustedgering(*flipface, CCW); // 'flipface' is bae.
  fnext(*flipface, abce);
  esymself(abce);
  adjustedgering(*flipface, CW); // 'flipface' is abe.
  fnext(*flipface, bade);
#ifdef SELF_CHECK
  assert(bade.tet != dummytet);
#endif
  esymself(bade);
  pa = org(abce);
  pb = dest(abce);
  pc = apex(abce);
  pd = apex(bade);
  pe = oppo(bade);
#ifdef SELF_CHECK
  assert(oppo(abce) == pe);
#endif
  sym(abce, bacf);
  mirrorflag = bacf.tet != dummytet;
  if (mirrorflag) {
    // findedge(&bacf, pb, pa);
    bacf.ver = 0;
    for (i = 0; (i < 3) && (org(bacf) != pb); i++) {
      enextself(bacf);
    }
    sym(bade, abdf);
#ifdef SELF_CHECK
    assert(abdf.tet != dummytet);
#endif
    // findedge(&abdf, pa, pb);
    abdf.ver = 0;
    for (i = 0; (i < 3) && (org(abdf) != pa); i++) {
      enextself(abdf);
    }
    pf = oppo(bacf);
#ifdef SELF_CHECK
    assert(oppo(abdf) == pf);
#endif
  } 

  if (b->verbose > 2) {
    printf("    Do %s on edge (%d, %d).\n", mirrorflag ? "T44" : "T22",
           pointmark(pa), pointmark(pb));
  }
  mirrorflag ? flip44s++ : flip22s++;

  // Save the old configuration at the convex hull.
  enextfnext(abce, oldbce);
  enext2fnext(abce, oldcae);
  enextfnext(bade, oldade);
  enext2fnext(bade, olddbe);
  sym(oldbce, bcecasing);
  sym(oldcae, caecasing);
  sym(oldade, adecasing);
  sym(olddbe, dbecasing);
  if (checksubfaces) {
    tspivot(oldbce, bcesh);
    tspivot(oldcae, caesh);
    tspivot(oldade, adesh);
    tspivot(olddbe, dbesh);
    tspivot(abce, abc);
    tspivot(bade, bad);
  } else if (checksubsegs) {
    // Coplanar segs: a->d->b->c.
    enext(bade, worktet); 
    tsspivot1(worktet, adseg);
    enext2(bade, worktet);
    tsspivot1(worktet, dbseg);
    enext(abce, worktet);
    tsspivot1(worktet, bcseg);
    enext2(abce, worktet);
    tsspivot1(worktet, caseg);
    // Above segs: a->e, d->e, b->e, c->e.
    fnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, aeseg);
    enextfnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, deseg);
    enext2fnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, beseg);
    enextfnext(abce, worktet);
    enextself(worktet);
    tsspivot1(worktet, ceseg);
  }
  if (mirrorflag) {
    enextfnext(bacf, oldacf);
    enext2fnext(bacf, oldcbf);
    enextfnext(abdf, oldbdf);
    enext2fnext(abdf, olddaf);
    sym(oldacf, acfcasing);
    sym(oldcbf, cbfcasing);
    sym(oldbdf, bdfcasing);
    sym(olddaf, dafcasing);
    if (checksubfaces) {
      tspivot(oldacf, acfsh);
      tspivot(oldcbf, cbfsh);
      tspivot(oldbdf, bdfsh);
      tspivot(olddaf, dafsh);
    } else if (checksubsegs) {
      // Below segs: a->f, d->f, b->f, c->f.
      fnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, afseg);
      enext2fnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, dfseg);
      enextfnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, bfseg);
      enextfnext(bacf, worktet);
      enextself(worktet);
      tsspivot1(worktet, cfseg);
    }
  }

  // Rotate abce, bade one-quarter turn counterclockwise.
  bond(oldbce, caecasing);
  bond(oldcae, adecasing);
  bond(oldade, dbecasing);
  bond(olddbe, bcecasing);
  if (checksubfaces) {
    // Check for subfaces and rebond them to the rotated tets.
    if (caesh.sh == dummysh) {
      tsdissolve(oldbce);
    } else {
      tsbond(oldbce, caesh);
    }
    if (adesh.sh == dummysh) {
      tsdissolve(oldcae);
    } else {
      tsbond(oldcae, adesh);
    }
    if (dbesh.sh == dummysh) {
      tsdissolve(oldade);
    } else {
      tsbond(oldade, dbesh);
    }
    if (bcesh.sh == dummysh) {
      tsdissolve(olddbe);
    } else {
      tsbond(olddbe, bcesh);
    }
  } else if (checksubsegs) {
    // 5 edges in abce are changed.
    enext(abce, worktet);  // fit b->c into c->a.
    if (caseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, caseg);
    }
    enext2(abce, worktet); // fit c->a into a->d.
    if (adseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, adseg);
    }
    fnext(abce, worktet); // fit b->e into c->e.
    enextself(worktet);
    if (ceseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, ceseg);
    }
    enextfnext(abce, worktet); // fit c->e into a->e.
    enextself(worktet);
    if (aeseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, aeseg);
    }
    enext2fnext(abce, worktet); // fit a->e into d->e.
    enextself(worktet);
    if (deseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, deseg);
    }
    // 5 edges in bade are changed.
    enext(bade, worktet); // fit a->d into d->b.
    if (dbseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, dbseg);
    }
    enext2(bade, worktet); // fit d->b into b->c.
    if (bcseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, bcseg);
    }
    fnext(bade, worktet); // fit a->e into d->e.
    enextself(worktet);
    if (deseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, deseg);
    }
    enextfnext(bade, worktet); // fit d->e into b->e.
    enextself(worktet);
    if (beseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, beseg);
    }
    enext2fnext(bade, worktet); // fit b->e into c->e.
    enextself(worktet);
    if (ceseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, ceseg);
    }
  }
  if (mirrorflag) {
    // Rotate bacf, abdf one-quarter turn counterclockwise.
    bond(oldcbf, acfcasing);
    bond(oldacf, dafcasing);
    bond(olddaf, bdfcasing);
    bond(oldbdf, cbfcasing);
    if (checksubfaces) {
      // Check for subfaces and rebond them to the rotated tets.
      if (acfsh.sh == dummysh) {
        tsdissolve(oldcbf);
      } else {
        tsbond(oldcbf, acfsh);
      }
      if (dafsh.sh == dummysh) {
        tsdissolve(oldacf);
      } else {
        tsbond(oldacf, dafsh);
      }
      if (bdfsh.sh == dummysh) {
        tsdissolve(olddaf);
      } else {
        tsbond(olddaf, bdfsh);
      }
      if (cbfsh.sh == dummysh) {
        tsdissolve(oldbdf);
      } else {
        tsbond(oldbdf, cbfsh);
      }
    } else if (checksubsegs) {
      // 5 edges in bacf are changed.
      enext2(bacf, worktet); // fit b->c into c->a.
      if (caseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, caseg);
      }
      enext(bacf, worktet); // fit c->a into a->d.
      if (adseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, adseg);
      }
      fnext(bacf, worktet); // fit b->f into c->f.
      enext2self(worktet);
      if (cfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, cfseg);
      }
      enext2fnext(bacf, worktet); // fit c->f into a->f.
      enext2self(worktet);
      if (afseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, afseg);
      }
      enextfnext(bacf, worktet); // fit a->f into d->f.
      enext2self(worktet);
      if (dfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dfseg);
      }
      // 5 edges in abdf are changed.
      enext2(abdf, worktet); // fit a->d into d->b.
      if (dbseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dbseg);
      }
      enext(abdf, worktet); // fit d->b into b->c.
      if (bcseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, bcseg);
      }
      fnext(abdf, worktet); // fit a->f into d->f.
      enext2self(worktet);
      if (dfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dfseg);
      }
      enext2fnext(abdf, worktet); // fit d->f into b->f.
      enext2self(worktet);
      if (bfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, bfseg);
      }
      enextfnext(abdf, worktet); // fit b->f into c->f.
      enext2self(worktet);
      if (cfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, cfseg);
      }
    }
  }

  // New vertex assignments for the rotated tetrahedra.
  setorg(abce, pd); // Update abce to dcae
  setdest(abce, pc);
  setapex(abce, pa);
  setorg(bade, pc); // Update bade to cdbe
  setdest(bade, pd);
  setapex(bade, pb);
  if (mirrorflag) {
    setorg(bacf, pc); // Update bacf to cdaf
    setdest(bacf, pd);
    setapex(bacf, pa);
    setorg(abdf, pd); // Update abdf to dcbf
    setdest(abdf, pc);
    setapex(abdf, pb);
  }

  // Are there subfaces need to be flipped?
  if (checksubfaces && abc.sh != dummysh) {
#ifdef SELF_CHECK
    assert(bad.sh != dummysh);
#endif
    // Adjust the edge be ab, so the rotation of subfaces is according with
    //   the rotation of tetrahedra.
    findedge(&abc, pa, pb);
    // Flip an edge of two subfaces, ignore non-Delaunay edges.
    flip22sub(&abc, NULL);
  }

  if (b->verbose > 3) {
    printf("    Updating abce ");
    printtet(&abce);
    printf("    Updating bade ");
    printtet(&bade);
    if (mirrorflag) {
      printf("    Updating bacf ");
      printtet(&bacf);
      printf("    Updating abdf ");
      printtet(&abdf);
    }
  }

  if (flipqueue != (queue *) NULL) { 
    enextfnext(abce, bcecasing);
    enqueueflipface(bcecasing, flipqueue);
    enext2fnext(abce, caecasing);
    enqueueflipface(caecasing, flipqueue);
    enextfnext(bade, adecasing);
    enqueueflipface(adecasing, flipqueue);
    enext2fnext(bade, dbecasing);
    enqueueflipface(dbecasing, flipqueue);
    if (mirrorflag) {
      enextfnext(bacf, acfcasing);
      enqueueflipface(acfcasing, flipqueue);
      enext2fnext(bacf, cbfcasing);
      enqueueflipface(cbfcasing, flipqueue);
      enextfnext(abdf, bdfcasing);
      enqueueflipface(bdfcasing, flipqueue);
      enext2fnext(abdf, dafcasing);
      enqueueflipface(dafcasing, flipqueue);
    }
    // The two new faces dcae (abce), cdbe (bade) may still not be locally
    //   Delaunay, and may need be flipped (flip23).  On the other hand, in
    //   conforming Delaunay algorithm, two new subfaces dca (abc), and cdb
    //   (bad) may be non-conforming Delaunay, they need be queued if they
    //   are locally Delaunay but non-conforming Delaunay.
    enqueueflipface(abce, flipqueue);
    enqueueflipface(bade, flipqueue);
  }

  // Save a live handle in 'recenttet'.
  recenttet = abce;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip22sub()    Perform a 2-to-2 flip on a subface edge.                   //
//                                                                           //
// The flip edge is given by subface 'flipedge'.  Let it is abc, where ab is //
// the flipping edge.  The other subface is bad,  where a, b, c, d form a    //
// convex quadrilateral.  ab is not a subsegment.                            //
//                                                                           //
// A 2-to-2 subface flip is to change two subfaces abc and bad to another    //
// two subfaces dca and cdb.  Hence, edge ab has been removed and dc becomes //
// an edge. If a point e is above abc, this flip is equal to rotate abc and  //
// bad counterclockwise using right-hand rule with thumb points to e. It is  //
// important to know that the edge rings of the flipped subfaces dca and cdb //
// are keeping the same orientation as their original subfaces. So they have //
// the same orientation with respect to the lift point of this facet.        //
//                                                                           //
// During rotating, the face rings of the four edges bc, ca, ad, and de need //
// be re-connected. If the edge is not a subsegment, then its face ring has  //
// only two faces, a sbond() will bond them together. If it is a subsegment, //
// one should use sbond1() twice to bond two different handles to the rotat- //
// ing subface, one is predecssor (-casin), another is successor (-casout).  //
//                                                                           //
// If 'flipqueue' is not NULL, it returns four edges bc, ca, ad, de, which   //
// may be non-Delaunay.                                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue)
{
  face abc, bad;
  face oldbc, oldca, oldad, olddb;
  face bccasin, bccasout, cacasin, cacasout;
  face adcasin, adcasout, dbcasin, dbcasout;
  face bc, ca, ad, db;
  face spinsh;
  point pa, pb, pc, pd;

  abc = *flipedge;
  spivot(abc, bad);
  if (sorg(bad) != sdest(abc)) {
    sesymself(bad);
  }
  pa = sorg(abc);
  pb = sdest(abc);
  pc = sapex(abc);
  pd = sapex(bad);

  if (b->verbose > 2) {
    printf("    Flip sub edge (%d, %d).\n", pointmark(pa), pointmark(pb));
  }

  // Save the old configuration outside the quadrilateral.  
  senext(abc, oldbc);
  senext2(abc, oldca);
  senext(bad, oldad);
  senext2(bad, olddb);
  // Get the outside connection. Becareful if there is a subsegment on the
  //   quadrilateral, two casings (casin and casout) are needed to save for
  //   keeping the face link.
  spivot(oldbc, bccasout);
  sspivot(oldbc, bc);
  if (bc.sh != dummysh) {
    // 'bc' is a subsegment.
    if (bccasout.sh != dummysh) {
      if (oldbc.sh != bccasout.sh) {
        // 'oldbc' is not self-bonded.
        spinsh = bccasout;
        do {
          bccasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldbc.sh);
      } else {
        bccasout.sh = dummysh;
      }
    }
    ssdissolve(oldbc);
  }
  spivot(oldca, cacasout);
  sspivot(oldca, ca);
  if (ca.sh != dummysh) {
    // 'ca' is a subsegment.
    if (cacasout.sh != dummysh) {
      if (oldca.sh != cacasout.sh) {
        // 'oldca' is not self-bonded.
        spinsh = cacasout;
        do {
          cacasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldca.sh);
      } else {
        cacasout.sh = dummysh;
      }
    }
    ssdissolve(oldca);
  }
  spivot(oldad, adcasout);
  sspivot(oldad, ad);
  if (ad.sh != dummysh) {
    // 'ad' is a subsegment. 
    if (adcasout.sh != dummysh) {
      if (oldad.sh != adcasout.sh) {
        // 'adcasout' is not self-bonded.
        spinsh = adcasout;
        do {
          adcasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldad.sh);
      } else {
        adcasout.sh = dummysh;
      }
    }
    ssdissolve(oldad);
  }
  spivot(olddb, dbcasout);
  sspivot(olddb, db);
  if (db.sh != dummysh) {
    // 'db' is a subsegment.
    if (dbcasout.sh != dummysh) {
      if (olddb.sh != dbcasout.sh) {
        // 'dbcasout' is not self-bonded.
        spinsh = dbcasout;
        do {
          dbcasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != olddb.sh);
      } else {
        dbcasout.sh = dummysh;
      }
    }
    ssdissolve(olddb);
  }

  // Rotate abc and bad one-quarter turn counterclockwise.
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      sbond1(cacasin, oldbc);
      sbond1(oldbc, cacasout);
    } else {
      // Bond 'oldbc' to itself.
      sbond(oldbc, oldbc);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldbc);
    }
    ssbond(oldbc, ca);
  } else {
    sbond(oldbc, cacasout);
  }
  if (ad.sh != dummysh) {
    if (adcasout.sh != dummysh) {
      sbond1(adcasin, oldca);
      sbond1(oldca, adcasout);
    } else {
      // Bond 'oldca' to itself.
      sbond(oldca, oldca);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldca);
    }
    ssbond(oldca, ad);
  } else {
    sbond(oldca, adcasout);
  }
  if (db.sh != dummysh) {
    if (dbcasout.sh != dummysh) {
      sbond1(dbcasin, oldad);
      sbond1(oldad, dbcasout);
    } else {
      // Bond 'oldad' to itself.
      sbond(oldad, oldad);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldad);
    }
    ssbond(oldad, db);
  } else {
    sbond(oldad, dbcasout);
  }
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      sbond1(bccasin, olddb);
      sbond1(olddb, bccasout);
    } else {
      // Bond 'olddb' to itself.
      sbond(olddb, olddb);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(olddb);
    }
    ssbond(olddb, bc);
  } else {
    sbond(olddb, bccasout);
  }

  // New vertex assignments for the rotated subfaces.
  setsorg(abc, pd);  // Update abc to dca.
  setsdest(abc, pc);
  setsapex(abc, pa);
  setsorg(bad, pc);  // Update bad to cdb.
  setsdest(bad, pd);
  setsapex(bad, pb);

  if (flipqueue != (queue *) NULL) {
    enqueueflipedge(bccasout, flipqueue);
    enqueueflipedge(cacasout, flipqueue);
    enqueueflipedge(adcasout, flipqueue);
    enqueueflipedge(dbcasout, flipqueue);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip()    Flips non-locally Delaunay faces in flipqueue until it is empty.//
//                                                                           //
// Assumpation:  Current tetrahedralization is non-Delaunay after inserting  //
// a point or performing a flip operation, all possibly non-Delaunay faces   //
// are in 'flipqueue'.                                                       //
//                                                                           //
// If 'plastflip' is not NULL,  it is used to return a stack of recently     //
// flipped faces.  This stack will be used to reverse the flips done in this //
// routine later for removing a newly inserted point because it encroaches   //
// any subfaces or subsegments.                                              //
//                                                                           //
// The return value is the total number of flips done during this invocation.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::flip(queue* flipqueue, badface **plastflip)
{
  badface *qface, *newflip;
  triface flipface, symface;
  point pa, pb, pc, pd, pe;
  enum fliptype fc;
  REAL sign, bakepsilon;
  long flipcount, maxfaces;
  int epscount, fcount;
  int ia, ib, ic, id, ie;

  if (b->verbose > 1) {
    printf("    Do flipface queue: %ld faces.\n", flipqueue->len());
  }

  flipcount = flip23s + flip32s + flip22s + flip44s;
  if (checksubfaces) {
    maxfaces = (4l * tetrahedrons->items + hullsize) / 2l;
    fcount = 0;
  }

  if (plastflip != (badface **) NULL) {
    // Initialize the stack of the flip sequence.
    flipstackers->restart();
    *plastflip = (badface *) NULL;
  }

  // Loop until the queue is empty.
  while (!flipqueue->empty()) {
    qface = (badface *) flipqueue->pop();
    flipface = qface->tt;
    if (isdead(&flipface)) continue;
    sym(flipface, symface);
    // Only do check when the adjacent tet exists and it's not a "fake" tet.
    if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) {
      // For positive orientation that insphere() test requires.
      adjustedgering(flipface, CW);
      pa = org(flipface);
      pb = dest(flipface);
      pc = apex(flipface);
      pd = oppo(flipface);
      pe = oppo(symface);
      if (symbolic) {
        ia = pointmark(pa);
        ib = pointmark(pb);
        ic = pointmark(pc);
        id = pointmark(pd);
        ie = pointmark(pe);
        sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie);
        assert(sign != 0.0);
      } else {  
        sign = insphere(pa, pb, pc, pd, pe);
      }
    } else {
      sign = -1.0; // A hull face is locally Delaunay.
    }
    if (sign > 0.0) {
      // 'flipface' is non-locally Delaunay, try to flip it.     
      if (checksubfaces) {
        fcount++;
        bakepsilon = b->epsilon;
        epscount = 0;
        while (epscount < 32) {
          fc = categorizeface(flipface);
          if (fc == N40) {
            b->epsilon *= 1e-1;
            epscount++;
            continue;
          }
          break;
        }
        b->epsilon = bakepsilon;
        if (epscount >= 32) {
          if (b->verbose > 0) {
            printf("Warning:  Can't flip a degenerate tetrahedron.\n");
          }
          fc = N40;
        }
      } else {
        fc = categorizeface(flipface);
#ifdef SELF_CHECK
        assert(fc != N40);
#endif
      }
      switch (fc) {
      // The following face types are flipable.
      case T44:
      case T22:
        flip22(&flipface, flipqueue); 
        break;
      case T23:
        flip23(&flipface, flipqueue); 
        break;
      case T32:
        flip32(&flipface, flipqueue); 
        break;
      // The following face types are unflipable.
      case N32:
        break;
      case FORBIDDENFACE:
        break;
      case FORBIDDENEDGE:
        break;
      // This case is only possible when the domain is nonconvex.
      case N40:
        // assert(nonconvex);
        break;
      }
      if (plastflip != (badface **) NULL) {
        if ((fc == T44) || (fc == T22) || (fc == T23) || (fc == T32)) { 
          // Push the flipped face into stack.
          newflip = (badface *) flipstackers->alloc();
          newflip->tt = flipface;
          newflip->key = (REAL) fc;
          newflip->forg = org(flipface);
          newflip->fdest = dest(flipface);
          newflip->fapex = apex(flipface);
          newflip->previtem = *plastflip;
          *plastflip = newflip; 
        }
      }
    }
  }

  flipcount = flip23s + flip32s + flip22s + flip44s - flipcount;
  if (b->verbose > 1) {
    printf("    %ld flips.\n", flipcount);
  }

  return flipcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lawson()    Flip locally non-Delaunay faces by Lawson's algorithm.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::lawson(list *misseglist, queue* flipqueue)
{
  badface *qface, *misseg;
  triface flipface, symface;
  triface starttet, spintet;
  face checksh, checkseg;
  point pa, pb, pc, pd, pe;
  point swappt;
  REAL sign, ori;
  long flipcount;
  int ia, ib, ic, id, ie;  
  int hitbdry, i;

  if (b->verbose > 1) {
    printf("    Do flipface queue: %ld faces.\n", flipqueue->len());
  }
  flipcount = flip23s + flip32s + flip22s + flip44s;

  // Go through the stack of possible flips and decide whether to do them.
  //   Note that during the loop new possible flips will be pushed onto
  //   this stack, while they popped in this loop.
  while (!flipqueue->empty()) {
    qface = (badface *) flipqueue->pop();
    flipface = qface->tt;
    // Check if tet has already been flipped out of existence.
    if (!isdead(&flipface)) {
      sym(flipface, symface);
      // Check if this tet is the same as the one which was stacked.
      if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) {
        flipface.ver = 0; // Select the CCW ring.
        pa = org(flipface);
        pb = dest(flipface);
        pc = apex(flipface);
        pd = oppo(flipface);
        pe = oppo(symface);
        if (symbolic) {
          ia = pointmark(pa);
          ib = pointmark(pb);
          ic = pointmark(pc);
          id = pointmark(pd);
          ie = pointmark(pe);
          sign = insphere_sos(pb, pa, pc, pd, pe, ib, ia, ic, id, ie);
        } else {
          sign = insphere(pb, pa, pc, pd, pe);
        }
        if (sign > 0.0) {
          for (i = 0; i < 3; i++) {
            ori = orient3d(pa, pb, pd, pe);
            if (ori > 0.0) {
              // Goto and check the next edge.
              swappt = pa;
              pa = pb;
              pb = pc;
              pc = swappt;
              enextself(flipface);
            } else {
              break; // either (ori < 0.0) or (ori == 0.0)
            }
          } // for (i = 0; ....)
          if (ori > 0.0) {
            // All three edges are convex, a 2-3 flip is possible.
            if (checksubfaces) {
              tspivot(flipface, checksh);
              if (checksh.sh != dummysh) {
                // A subface is not flipable.
                continue;
              }
            }
            flip23(&flipface, flipqueue);
          } else if (ori < 0.0) {
            // The edge (a, b) is non-convex, check for a 3-2 flip.
            fnext(flipface, symface);
            symself(symface);
            if (oppo(symface) == pe) {
              // Only three tets adjoining this edge.
              if (checksubfaces) {
                tsspivot(&flipface, &checkseg);
                if (checkseg.sh != dummysh) {
                  // A subsegment is not flipable.
                  continue;
                }
              } else if (checksubsegs) {
                tsspivot1(flipface, checkseg);
                if (checkseg.sh != dummysh) {
                  if (b->verbose > 2) {
                    printf("    Queuing missing segment (%d, %d).\n",
                      pointmark(org(flipface)), pointmark(dest(flipface)));
                  }
                  misseg = (badface *) misseglist->append(NULL);
                  misseg->ss = checkseg;
                  misseg->forg = sorg(checkseg);
                  misseg->fdest = sdest(checkseg);
                  // Detach all tets having this seg.
                  starttet = flipface;
                  adjustedgering(starttet, CCW);
                  fnextself(starttet);
                  spintet = starttet;
                  hitbdry = 0;
                  do {
                    tssdissolve1(spintet);
                    if (!fnextself(spintet)) {
                      hitbdry++;
                      if (hitbdry < 2) {
                        esym(starttet, spintet);
                        if (!fnextself(spintet)) {
                          hitbdry++;
                        }
                      }
                    }
                  } while ((apex(spintet) != apex(starttet)) && (hitbdry < 2));
                }
              } // if (checksubfaces)
              flip32(&flipface, flipqueue);
            }
          } else {
            // Four points (a, b, d, e) are coplanar.
            fnext(flipface, symface);
            if (fnextself(symface)) {
              // Check for a 4-4 flip.
              fnextself(symface);
              if (apex(symface) == pe) {
                if (checksubfaces) {
                  tsspivot(&flipface, &checkseg);
                  if (checkseg.sh != dummysh) {
                    // A subsegment is not flippable.
                    continue;
                  }
                } else if (checksubsegs) {
                  tsspivot1(flipface, checkseg);
                  if (checkseg.sh != dummysh) {
                    if (b->verbose > 2) {
                      printf("    Queuing missing segment (%d, %d).\n",
                        pointmark(org(flipface)), pointmark(dest(flipface)));
                    }
                    misseg = (badface *) misseglist->append(NULL);
                    misseg->ss = checkseg;
                    misseg->forg = sorg(checkseg);
                    misseg->fdest = sdest(checkseg);
                    // Detach all tets having this seg.
                    starttet = flipface;
                    adjustedgering(starttet, CCW);
                    fnextself(starttet);
                    spintet = starttet;
                    hitbdry = 0;
                    do {
                      tssdissolve1(spintet);
                      if (!fnextself(spintet)) {
                        hitbdry++;
                        if (hitbdry < 2) {
                          esym(starttet, spintet);
                          if (!fnextself(spintet)) {
                            hitbdry++;
                          }
                        }
                      }
                    } while ((apex(spintet) != apex(starttet)) && 
                             (hitbdry < 2));
                  }
                } // if (checksubfaces) 
                flip22(&flipface, flipqueue);
              }
            } else {
              // Check for a 2-2 flip.
              esym(flipface, symface);
              fnextself(symface);
              symself(symface);
              if (symface.tet == dummytet) {
                if (checksubfaces) {
                  tsspivot(&flipface, &checkseg);
                  if (checkseg.sh != dummysh) {
                    // A subsegment is not flipable.
                    continue;
                  }
                } else if (checksubsegs) {
                  tsspivot1(flipface, checkseg);
                  if (checkseg.sh != dummysh) {
                    if (b->verbose > 2) {
                      printf("    Queuing missing segment (%d, %d).\n",
                        pointmark(org(flipface)), pointmark(dest(flipface)));
                    }
                    misseg = (badface *) misseglist->append(NULL);
                    misseg->ss = checkseg;
                    misseg->forg = sorg(checkseg);
                    misseg->fdest = sdest(checkseg);
                    // Detach all tets having this seg.
                    starttet = flipface;
                    adjustedgering(starttet, CCW);
                    fnextself(starttet);
                    spintet = starttet;
                    hitbdry = 0;
                    do {
                      tssdissolve1(spintet);
                      if (!fnextself(spintet)) {
                        hitbdry++;
                        if (hitbdry < 2) {
                          esym(starttet, spintet);
                          if (!fnextself(spintet)) {
                            hitbdry++;
                          }
                        }
                      }
                    } while ((apex(spintet) != apex(starttet)) && 
                             (hitbdry < 2));
                  }
                } // if (checksubfaces)
                flip22(&flipface, flipqueue);
              }
            }
          } // if (ori > 0.0)
        } // if (sign > 0.0)
      }
    } // !isdead(&qface->tt)
  } // while (!flipqueue->empty())

  flipcount = flip23s + flip32s + flip22s + flip44s - flipcount;
  if (b->verbose > 1) {
    printf("    %ld flips.\n", flipcount);
  }
  return flipcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// undoflip()    Undo the most recent flip sequence induced by flip().       //
//                                                                           //
// 'lastflip' is the stack of recently flipped faces. Walks through the list //
// of flips, in the reverse of the order in which they were done, and undoes //
// them.                                                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::undoflip(badface *lastflip)
{
  enum fliptype fc;

  while (lastflip != (badface *) NULL) {
    // Get the right flipped face.
    findface(&lastflip->tt, lastflip->forg, lastflip->fdest, lastflip->fapex);
    fc = (enum fliptype) (int) lastflip->key;
    switch (fc) {
    case T23:
      // The reverse operation of T23 is T32.
      flip32(&lastflip->tt, NULL);
      break;
    case T32:
      // The reverse operation of T32 is T23.
      flip23(&lastflip->tt, NULL);
      break;
    case T22:
    case T44:
      // The reverse operation of T22 or T44 is again T22 or T44.
      flip22(&lastflip->tt, NULL);
      break;
    default: // To omit compile warnings.
      break;
    }
    // Go on and process the next transformation.
    lastflip = lastflip->previtem;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipsub()    Flip non-Delaunay edges in a queue of (coplanar) subfaces.   //
//                                                                           //
// Assumpation:  Current triangulation T contains non-Delaunay edges (after  //
// inserting a point or performing a flip). Non-Delaunay edges are queued in //
// 'facequeue'. Returns the total number of flips done during this call.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::flipsub(queue* flipqueue)
{
  badface *qedge;
  face flipedge, symedge;
  face checkseg;
  point pa, pb, pc, pd;
  REAL vab[3], vac[3], vad[3];
  REAL dot1, dot2, lac, lad; 
  REAL sign, ori;
  int edgeflips;
  int i;

  if (b->verbose > 1) {
    printf("  Start do edge queue: %ld edges.\n", flipqueue->len());
  }

  edgeflips = 0;

  while (!flipqueue->empty()) {
    qedge = (badface *) flipqueue->pop();
    flipedge = qedge->ss;
    if (flipedge.sh == dummysh) continue;
    if ((sorg(flipedge) != qedge->forg) || 
        (sdest(flipedge) != qedge->fdest)) continue; 
    sspivot(flipedge, checkseg);
    if (checkseg.sh != dummysh) continue;  // Can't flip a subsegment.
    spivot(flipedge, symedge);
    if (symedge.sh == dummysh) continue; // Can't flip a hull edge.
    pa = sorg(flipedge);
    pb = sdest(flipedge);
    pc = sapex(flipedge);
    pd = sapex(symedge);
    // Choose the triangle abc or abd as the base depending on the angle1
    //   (Vac, Vab) and angle2 (Vad, Vab).
    for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i];
    for (i = 0; i < 3; i++) vac[i] = pc[i] - pa[i];
    for (i = 0; i < 3; i++) vad[i] = pd[i] - pa[i];
    dot1 = dot(vac, vab);
    dot2 = dot(vad, vab);
    dot1 *= dot1;
    dot2 *= dot2;
    lac = dot(vac, vac);
    lad = dot(vad, vad);
    if (lad * dot1 <= lac * dot2) {
      // angle1 is closer to 90 than angle2, choose abc (flipedge).
      abovepoint = facetabovepointarray[shellmark(flipedge)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&flipedge);
      }
      sign = insphere(pa, pb, pc, abovepoint, pd);
      ori = orient3d(pa, pb, pc, abovepoint);
    } else {
      // angle2 is closer to 90 than angle1, choose abd (symedge).
      abovepoint = facetabovepointarray[shellmark(symedge)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&symedge);
      }
      sign = insphere(pa, pb, pd, abovepoint, pc);
      ori = orient3d(pa, pb, pd, abovepoint);
    }
    // Correct the sign.
    sign = ori > 0.0 ? sign : -sign;
    if (sign > 0.0) {
      // Flip the non-Delaunay edge.
      flip22sub(&flipedge, flipqueue);
      edgeflips++;
    }
  }

  if (b->verbose > 1) {
    printf("  Total %d flips.\n", edgeflips);
  }

  return edgeflips;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removetetbypeeloff()    Remove a boundary tet by peeling it off.          //
//                                                                           //
// 'striptet' (abcd) is on boundary and can be removed by stripping it off.  //
// Let abc and bad are the external boundary faces.                          //
//                                                                           //
// To strip 'abcd' from the mesh is to detach its two interal faces (dca and //
// cdb) from their adjoining tets together with a 2-to-2 flip to transform   //
// two subfaces (abc and bad) into another two (dca and cdb).                //
//                                                                           //
// In mesh optimization. It is possible that ab is a segment and abcd is a   //
// sliver on the hull. Strip abcd will also delete the segment ab.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removetetbypeeloff(triface *striptet)
{
  triface abcd, badc;
  triface dcacasing, cdbcasing;
  face abc, bad;
  face abseg;
  REAL ang;
  
  abcd = *striptet;
  adjustedgering(abcd, CCW);
  // Get the casing tets at the internal sides.
  enextfnext(abcd, cdbcasing);
  enext2fnext(abcd, dcacasing);
  symself(cdbcasing);
  symself(dcacasing);  
  // Do the neighboring tets exist?  During optimization. It is possible
  //   that the neighboring tets are already dead.
  if ((cdbcasing.tet == dummytet) || (dcacasing.tet == dummytet)) {
    // Do not strip this tet.
    return false;
  }

  // Are there subfaces?
  if (checksubfaces) {
    // Get the external subfaces abc, bad.
    fnext(abcd, badc);
    esymself(badc);
    tspivot(abcd, abc);
    tspivot(badc, bad);
    if (abc.sh != dummysh) { 
      assert(bad.sh != dummysh);
      findedge(&abc, org(abcd), dest(abcd));
      findedge(&bad, org(badc), dest(badc));
      // Is ab a segment?
      sspivot(abc, abseg);
      if (abseg.sh != dummysh) {
        // Does a segment allow to be removed?
        if ((b->optlevel > 3) && (b->nobisect == 0)) {
          // Only remove this segment if the dihedal angle at ab is between
          //   [b->maxdihedral-9, 180] (deg).  This avoids mistakely fliping
          //   ab when it has actually no big dihedral angle while cd has.
          ang = facedihedral(org(abcd), dest(abcd), apex(abcd), oppo(abcd));
          ang = ang * 180.0 / PI;
          if ((ang + 9.0) > b->maxdihedral) {
            if (b->verbose > 1) {
              printf("    Remove a segment during peeling.\n");
            }
            face prevseg, nextseg;
            // It is only shared by abc and bad (abcd is a tet).
            ssdissolve(abc);
            ssdissolve(bad);
            abseg.shver = 0;
            senext(abseg, nextseg);
            spivotself(nextseg);
            if (nextseg.sh != dummysh) {
              ssdissolve(nextseg);
            }
            senext2(abseg, prevseg);
            spivotself(prevseg);
            if (prevseg.sh != dummysh) {
              ssdissolve(prevseg);
            }
            shellfacedealloc(subsegs, abseg.sh);
            optcount[1]++;
          } else {
            return false;
          }
        } else {
          return false;
        }
      }
      // Do a 2-to-2 flip on abc and bad, transform abc->dca, bad->cdb.
      flip22sub(&abc, NULL);
      // The two internal faces become boundary faces.
      tsbond(cdbcasing, bad);
      tsbond(dcacasing, abc);
    }
  }
  
  // Detach abcd from the two internal faces.
  dissolve(cdbcasing);
  dissolve(dcacasing);
  // Delete abcd.
  tetrahedrondealloc(abcd.tet);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflip22()    Remove an edge by a 2-to-2 (or 4-to-4) flip.      //
//                                                                           //
// 'abtetlist' contains n tets (n is 2 or 4) sharing edge ab,  abtetlist[0]  //
// and abtetlist[1] are tets abec and abde, respectively (NOTE, both are in  //
// CW edge ring), where a, b, c, and d are coplanar.  If n = 4, abtetlist[2] //
// and abtetlist[3] are tets abfd and abcf, respectively.  This routine uses //
// flip22() to replace edge ab with cd, the surrounding tets are rotated.    //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebyflip22(REAL *key, int n, triface *abtetlist,
  queue *flipque)
{
  point pa, pb, pc, pd, pe, pf;
  REAL cosmaxd, d1, d2, d3;
  bool doflip;

  doflip = true;
  adjustedgering(abtetlist[0], CW);
  pa = org(abtetlist[0]);
  pb = dest(abtetlist[0]);
  pe = apex(abtetlist[0]);
  pc = oppo(abtetlist[0]);
  pd = apex(abtetlist[1]);
  if (n == 4) {
    pf = apex(abtetlist[2]);
  }
  if (key && (*key > -1.0)) {
    tetalldihedral(pc, pd, pe, pa, NULL, &d1, NULL);
    tetalldihedral(pd, pc, pe, pb, NULL, &d2, NULL);
    cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
    if (n == 4) {
      tetalldihedral(pd, pc, pf, pa, NULL, &d1, NULL);
      tetalldihedral(pc, pd, pf, pb, NULL, &d2, NULL);
      d3 = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle.
    }
    doflip = (*key < cosmaxd); // Can local quality be improved?
  }

  if (doflip) {
    flip22(&abtetlist[0], NULL);
    // Return the improved quality value.
    if (key) *key = cosmaxd;
  }

  return doflip;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removefacebyflip23()    Remove a face by a 2-to-3 flip.                   //
//                                                                           //
// 'abctetlist' contains 2 tets sharing abc, which are [0]abcd and [1]bace.  //
// This routine forms three new tets that abc is not a face anymore. Save    //
// them in 'newtetlist': [0]edab, [1]edbc, and [2]edca.  Note that the new   //
// tets may not valid if one of them get inverted. return false if so.       //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
// If the face is flipped, 'newtetlist' returns the three new tets. The two  //
// tets in 'abctetlist' are NOT deleted.  The caller has the right to either //
// delete them or reverse the operation.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removefacebyflip23(REAL *key, triface *abctetlist,
  triface *newtetlist, queue *flipque)
{
  triface edab, edbc, edca; // new configuration.
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL ori, cosmaxd, d1, d2, d3;
  REAL attrib, volume;
  bool doflip;
  int i;

  adjustedgering(abctetlist[0], CCW);
  pa = org(abctetlist[0]);
  pb = dest(abctetlist[0]);
  pc = apex(abctetlist[0]);
  pd = oppo(abctetlist[0]);  
  pe = oppo(abctetlist[1]);

  // Check if the flip creates valid new tets.
  ori = orient3d(pe, pd, pa, pb);
  if (ori < 0.0) {
    ori = orient3d(pe, pd, pb, pc);
    if (ori < 0.0) {
      ori = orient3d(pe, pd, pc, pa);
    }
  }
  doflip = (ori < 0.0); // Can abc be flipped away?
  if (doflip && (key != (REAL *) NULL)) {
    if (*key > -1.0) {
      // Test if the new tets reduce the maximal dihedral angle.
      tetalldihedral(pe, pd, pa, pb, NULL, &d1, NULL);
      tetalldihedral(pe, pd, pb, pc, NULL, &d2, NULL);
      tetalldihedral(pe, pd, pc, pa, NULL, &d3, NULL);
      cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle.
      doflip = (*key < cosmaxd); // Can local quality be improved?
    }
  }

  if (doflip) {
    // A valid (2-to-3) flip is found.
    flip23s++;
    // Create the new tets.
    maketetrahedron(&edab);
    setorg(edab, pe);
    setdest(edab, pd);
    setapex(edab, pa);
    setoppo(edab, pb);
    maketetrahedron(&edbc);
    setorg(edbc, pe);
    setdest(edbc, pd);
    setapex(edbc, pb);
    setoppo(edbc, pc);
    maketetrahedron(&edca);
    setorg(edca, pe);
    setdest(edca, pd);
    setapex(edca, pc);
    setoppo(edca, pa);
    // Transfer the element attributes.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(abctetlist[0].tet, i);
      setelemattribute(edab.tet, i, attrib);
      setelemattribute(edbc.tet, i, attrib);
      setelemattribute(edca.tet, i, attrib);
    }
    // Transfer the volume constraints.
    if (b->varvolume && !b->refine) {
      volume = volumebound(abctetlist[0].tet);
      setvolumebound(edab.tet, volume);
      setvolumebound(edbc.tet, volume);
      setvolumebound(edca.tet, volume);
    }
    // Return two new tets.
    newtetlist[0] = edab;
    newtetlist[1] = edbc;
    newtetlist[2] = edca;
    // Glue the three new tets.
    for (i = 0; i < 3; i++) {
      fnext(newtetlist[i], newfront);
      bond(newfront, newtetlist[(i + 1) % 3]);
    }
    // Substitute the three new tets into the old cavity.
    for (i = 0; i < 3; i++) {
      fnext(abctetlist[0], oldfront);
      sym(oldfront, adjfront); // may be outside.
      enextfnext(newtetlist[i], newfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enextself(abctetlist[0]);
    }
    findedge(&(abctetlist[1]), pb, pa);
    for (i = 0; i < 3; i++) {
      fnext(abctetlist[1], oldfront);
      sym(oldfront, adjfront); // may be outside.
      enext2fnext(newtetlist[i], newfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enext2self(abctetlist[1]);
    }
    // Do not delete the old tets.
    // for (i = 0; i < 2; i++) {
    //   tetrahedrondealloc(abctetlist[i].tet);
    // }
    // Return the improved quality value.
    if (key != (REAL *) NULL) *key = cosmaxd;
    return true;
  }

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflip32()    Remove an edge by a 3-to-2 flip.                  //
//                                                                           //
// 'abtetlist' contains 3 tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, where a lies in front of and b lies behind it. The 3 tets  //
// of the list are: [0]abce, [1]abdc, and [2]abed, respectively.             //
//                                                                           //
// This routine forms two new tets that ab is not an edge of them. Save them //
// in 'newtetlist', [0]dcea, [1]cdeb. Note that the new tets may not valid   //
// if one of them get inverted. return false if so.                          //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
// If the edge is flipped, 'newtetlist' returns the two new tets. The three  //
// tets in 'abtetlist' are NOT deleted.  The caller has the right to either  //
// delete them or reverse the operation.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist,
  triface *newtetlist, queue *flipque)
{
  triface dcea, cdeb; // new configuration.
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL ori, cosmaxd, d1, d2;
  REAL attrib, volume;
  bool doflip;
  int i;

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

  ori = orient3d(pd, pc, pe, pa);
  if (ori < 0.0) {
    ori = orient3d(pc, pd, pe, pb);
  }
  doflip = (ori < 0.0); // Can ab be flipped away?

  // Does the caller ensure a valid configuration?
  if (doflip && (key != (REAL *) NULL)) {    
    if (*key > -1.0) {
      // Test if the new tets reduce the maximal dihedral angle.
      tetalldihedral(pd, pc, pe, pa, NULL, &d1, NULL);
      tetalldihedral(pc, pd, pe, pb, NULL, &d2, NULL);
      cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      doflip = (*key < cosmaxd); // Can local quality be improved?
      // Return the key
      *key = cosmaxd;
    }
  }

  if (doflip) {
    // Create the new tets.
    maketetrahedron(&dcea);
    setorg(dcea, pd);
    setdest(dcea, pc);
    setapex(dcea, pe);
    setoppo(dcea, pa);
    maketetrahedron(&cdeb);
    setorg(cdeb, pc);
    setdest(cdeb, pd);
    setapex(cdeb, pe);
    setoppo(cdeb, pb);
    // Transfer the element attributes.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(abtetlist[0].tet, i);
      setelemattribute(dcea.tet, i, attrib);
      setelemattribute(cdeb.tet, i, attrib);
    }
    // Transfer the volume constraints.
    if (b->varvolume && !b->refine) {
      volume = volumebound(abtetlist[0].tet);
      setvolumebound(dcea.tet, volume);
      setvolumebound(cdeb.tet, volume);
    }
    // Return two new tets.
    newtetlist[0] = dcea;
    newtetlist[1] = cdeb;
    // Glue the two new tets.
    bond(dcea, cdeb);
    // Substitute the two new tets into the old three-tets cavity.
    for (i = 0; i < 3; i++) {
      fnext(dcea, newfront); // face dca, cea, eda.
      esym(abtetlist[(i + 1) % 3], oldfront);
      enextfnextself(oldfront);
      // Get the adjacent tet at the face (may be a dummytet).
      sym(oldfront, adjfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enext2self(dcea);
    }
    for (i = 0; i < 3; i++) {
      fnext(cdeb, newfront); // face cdb, deb, ecb.
      esym(abtetlist[(i + 1) % 3], oldfront);
      enext2fnextself(oldfront);
      // Get the adjacent tet at the face (may be a dummytet).
      sym(oldfront, adjfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enextself(cdeb);
    }
    // Do not delete the old tets.
    // for (i = 0; i < 3; i++) {
    //   tetrahedrondealloc(abtetlist[i].tet);
    // }
    return true;
  } // if (doflip)

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebytranNM()    Remove an edge by transforming n-to-m tets.       //
//                                                                           //
// This routine attempts to remove a given edge (ab) by transforming the set //
// T of tets surrounding ab into another set T' of tets.  T and T' have the  //
// same outer faces and ab is not an edge of T' anymore. Let |T|=n, and |T'| //
// =m, it is actually a n-to-m flip for n > 3.  The relation between n and m //
// depends on the method, ours is found below.                               //
//                                                                           //
// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, where a lies in front of and b lies behind it.  Let the    //
// projections of the n apexes onto screen in clockwise order are: p_0, ...  //
// p_n-1, respectively. The tets in the list are: [0]abp_0p_n-1,[1]abp_1p_0, //
// ..., [n-1]abp_n-1p_n-2, respectively.                                     //
//                                                                           //
// The principle of the approach is: Recursively reduce the link of ab by    //
// using flip23 until only three faces remain, hence a flip32 can be applied //
// to remove ab. For a given face a.b.p_0, check a flip23 can be applied on  //
// it, i.e, edge p_1.p_n-1 crosses it. NOTE*** We do the flip even p_1.p_n-1 //
// intersects with a.b (they are coplanar). If so, a degenerate tet (a.b.p_1.//
// p_n-1) is temporarily created, but it will be eventually removed by the   //
// final flip32. This relaxation splits a flip44 into flip23 + flip32. *NOTE //
// Now suppose a.b.p_0 gets flipped, p_0 is not on the link of ab anymore.   //
// The link is then reduced (by 1). 2 of the 3 new tets, p_n-1.p_1.p_0.a and //
// p_1.p_n-1.p_0.b, will be part of the new configuration.  The left new tet,//
// a.b.p_1.p_n-1, goes into the new link of ab. A recurrence can be applied. //
//                                                                           //
// If 'e1' and 'e2' are not NULLs, they specify an wanted edge to appear in  //
// the new tet configuration. In such case, only do flip23 if edge e1<->e2   //
// can be recovered. It is used in removeedgebycombNM().                     //
//                                                                           //
// If ab gets removed. 'newtetlist' contains m new tets.  By using the above //
// approach, the pairs (n, m) can be easily enumerated.  For example, (3, 2),//
// (4, 4), (5, 6), (6, 8), (7, 10), (8, 12), (9, 14), (10, 16),  and so on.  //
// It is easy to deduce, that m = (n - 2) * 2, when n >= 3.  The n tets in   //
// 'abtetlist' are NOT deleted in this routine. The caller has the right to  //
// either delete them or reverse this operation.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist,
  triface *newtetlist, point e1, point e2, queue *flipque)
{
  triface tmpabtetlist[9]; // Temporary max 9 tets configuration.
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, p[10];
  REAL ori, cosmaxd, d1, d2;
  REAL tmpkey;
  REAL attrib, volume;
  bool doflip, copflag, success;
  int i, j, k;

  // Maximum 10 tets.
  assert(n <= 10);
  // Two points a and b are fixed.
  pa = org(abtetlist[0]);
  pb = dest(abtetlist[0]);
  // The points p_0, p_1, ..., p_n-1 are permuted in each new configuration.
  //   These permutations can be easily done in the following loop.
  // Loop through all the possible new tets configurations. Stop on finding
  //   a valid new tet configuration which also immproves the quality value.
  for (i = 0; i < n; i++) {
    // Get other n points for the current configuration.
    for (j = 0; j < n; j++) {
      p[j] = apex(abtetlist[(i + j) % n]);
    }
    // Is there a wanted edge?
    if ((e1 != (point) NULL) && (e2 != (point) NULL)) {
      // Yes. Skip this face if p[1]<->p[n-1] is not the edge.
      if (!(((p[1] == e1) && (p[n - 1] == e2)) ||
	    ((p[1] == e2) && (p[n - 1] == e1)))) continue;
    }
    // Test if face a.b.p_0 can be flipped (by flip23), ie, to check if the
    //   edge p_n-1.p_1 crosses face a.b.p_0 properly.
    // Note. It is possible that face a.b.p_0 has type flip44, ie, a,b,p_1,
    //   and p_n-1 are coplanar. A trick is to split the flip44 into two
    //   steps: frist a flip23, then a flip32. The first step creates a
    //   degenerate tet (vol=0) which will be removed by the second flip.
    ori = orient3d(pa, pb, p[1], p[n - 1]);
    copflag = (ori == 0.0); // Are they coplanar?
    if (ori >= 0.0) {
      // Accept the coplanar case which supports flip44.
      ori = orient3d(pb, p[0], p[1], p[n - 1]);
      if (ori > 0.0) {
        ori = orient3d(p[0], pa, p[1], p[n - 1]);
      }
    }
    // Is face abc flipable?
    if (ori > 0.0) {
      // A valid (2-to-3) flip (or 4-to-4 flip) is found.
      copflag ? flip44s++ : flip23s++;
      doflip = true;
      if (key != (REAL *) NULL) {
        if (*key > -1.0) {
          // Test if the new tets reduce the maximal dihedral angle. Only 2
          //   tets, p_n-1.p_1.p_0.a and p_1.p_n-1.p_0.b, need to be tested
          //   The left one a.b.p_n-1.p_1 goes into the new link of ab.
          tetalldihedral(p[n - 1], p[1], p[0], pa, NULL, &d1, NULL);
          tetalldihedral(p[1], p[n - 1], p[0], pb, NULL, &d2, NULL);
          cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
          doflip = *key < cosmaxd; // Can the local quality be improved?
        }
      }
      if (doflip) {
        tmpkey = key != NULL ? *key : -1.0;
        // Create the two new tets.
        maketetrahedron(&(newtetlist[0]));
        setorg(newtetlist[0], p[n - 1]);
        setdest(newtetlist[0], p[1]);
        setapex(newtetlist[0], p[0]);
        setoppo(newtetlist[0], pa);
        maketetrahedron(&(newtetlist[1]));
        setorg(newtetlist[1], p[1]);
        setdest(newtetlist[1], p[n - 1]);
        setapex(newtetlist[1], p[0]);
        setoppo(newtetlist[1], pb);
        // Create the n - 1 temporary new tets (the new Star(ab)).
        maketetrahedron(&(tmpabtetlist[0]));
        setorg(tmpabtetlist[0], pa);
        setdest(tmpabtetlist[0], pb);
        setapex(tmpabtetlist[0], p[n - 1]);
        setoppo(tmpabtetlist[0], p[1]);
        for (j = 1; j < n - 1; j++) {
          maketetrahedron(&(tmpabtetlist[j]));
          setorg(tmpabtetlist[j], pa);
          setdest(tmpabtetlist[j], pb);
          setapex(tmpabtetlist[j], p[j]);
          setoppo(tmpabtetlist[j], p[j + 1]);
        }
        // Transfer the element attributes.
        for (j = 0; j < in->numberoftetrahedronattributes; j++) {
          attrib = elemattribute(abtetlist[0].tet, j);
          setelemattribute(newtetlist[0].tet, j, attrib);
          setelemattribute(newtetlist[1].tet, j, attrib);
          for (k = 0; k < n - 1; k++) {
            setelemattribute(tmpabtetlist[k].tet, j, attrib);
          }
        }
        // Transfer the volume constraints.
        if (b->varvolume && !b->refine) {
          volume = volumebound(abtetlist[0].tet);
          setvolumebound(newtetlist[0].tet, volume);
          setvolumebound(newtetlist[1].tet, volume);
          for (k = 0; k < n - 1; k++) {
            setvolumebound(tmpabtetlist[k].tet, volume);
          }
        }
        // Glue the new tets at their internal faces: 2 + (n - 1).
        bond(newtetlist[0], newtetlist[1]); // p_n-1.p_1.p_0.
        fnext(newtetlist[0], newfront);
        enext2fnext(tmpabtetlist[0], adjfront);
        bond(newfront, adjfront); // p_n-1.p_1.a.
        fnext(newtetlist[1], newfront);
        enextfnext(tmpabtetlist[0], adjfront);
        bond(newfront, adjfront); // p_n-1.p_1.b.
        // Glue n - 1 internal faces around ab.
        for (j = 0; j < n - 1; j++) {
          fnext(tmpabtetlist[j], newfront);
          bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1
        }
        // Substitute the old tets with the new tets by connecting the new
        //   tets to the adjacent tets in the mesh. There are n * 2 (outer)
        //   faces of the new tets need to be operated.
        // Note, after the substitution, the old tets still have pointers to
        //   their adjacent tets in the mesh.  These pointers can be re-used
        //   to inverse the substitution.
        for (j = 0; j < n; j++) {
          // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0).
          oldfront = abtetlist[(i + j) % n];
          esymself(oldfront);
          enextfnextself(oldfront);
          // Get an adjacent tet at face: [0]a.p_0.p_n-1 or [j]a.p_j.p_j-1.
          sym(oldfront, adjfront); // adjfront may be dummy.
          // Get the corresponding face from the new tets.
          if (j == 0) {
            enext2fnext(newtetlist[0], newfront); // a.p_0.n_n-1
          } else if (j == 1) {
            enextfnext(newtetlist[0], newfront); // a.p_1.p_0
          } else { // j >= 2.
            enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1
          }
          bond(newfront, adjfront);
          if (checksubfaces) {
            tspivot(oldfront, checksh); 
            if (checksh.sh != dummysh) {
              tsbond(newfront, checksh);
            }
          }
          if (flipque != (queue *) NULL) {
            // Only queue the faces of the two new tets.
            if (j < 2) enqueueflipface(newfront, flipque);
          }
        }
        for (j = 0; j < n; j++) {
          // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0).
          oldfront = abtetlist[(i + j) % n];
          esymself(oldfront);
          enext2fnextself(oldfront);
          // Get an adjacent tet at face: [0]b.p_0.p_n-1 or [j]b.p_j.p_j-1.
          sym(oldfront, adjfront); // adjfront may be dummy.
          // Get the corresponding face from the new tets.
          if (j == 0) {
            enextfnext(newtetlist[1], newfront); // b.p_0.n_n-1
          } else if (j == 1) {
            enext2fnext(newtetlist[1], newfront); // b.p_1.p_0
          } else { // j >= 2.
            enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1
          }
          bond(newfront, adjfront);
          if (checksubfaces) {
            tspivot(oldfront, checksh); 
            if (checksh.sh != dummysh) {
              tsbond(newfront, checksh);
            }
          }
          if (flipque != (queue *) NULL) {
            // Only queue the faces of the two new tets.
            if (j < 2) enqueueflipface(newfront, flipque);
          }
        }
        // Adjust the faces in the temporary new tets at ab for recursively
        //   processing on the n-1 tets.(See the description at beginning)
        for (j = 0; j < n - 1; j++) {
          fnextself(tmpabtetlist[j]);
        }
        if (n > 4) {
          success = removeedgebytranNM(&tmpkey, n-1, tmpabtetlist,
            &(newtetlist[2]), NULL, NULL, flipque);
        } else { // assert(n == 4);
          success = removeedgebyflip32(&tmpkey, tmpabtetlist,
            &(newtetlist[2]), flipque);
        }
        // No matter it was success or not, delete the temporary tets.
        for (j = 0; j < n - 1; j++) {
          tetrahedrondealloc(tmpabtetlist[j].tet);
        }
        if (success) {
          // The new configuration is good. 
          // Do not delete the old tets.
          // for (j = 0; j < n; j++) {
          //   tetrahedrondealloc(abtetlist[j].tet);
          // }
          // Save the minimal improved quality value.
          if (key != (REAL *) NULL) {
            *key = (tmpkey < cosmaxd ? tmpkey : cosmaxd);
          }
          return true;
        } else {
          // The new configuration is bad, substitue back the old tets.
          for (j = 0; j < n; j++) {
            oldfront = abtetlist[(i + j) % n];
            esymself(oldfront);
            enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1.
            sym(oldfront, adjfront); // adjfront may be dummy.
            bond(oldfront, adjfront);
            if (checksubfaces) {
              tspivot(oldfront, checksh);
              if (checksh.sh != dummysh) {
                tsbond(oldfront, checksh);
              }
            }
          }
          for (j = 0; j < n; j++) {
            oldfront = abtetlist[(i + j) % n];
            esymself(oldfront);
            enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
            sym(oldfront, adjfront); // adjfront may be dummy
            bond(oldfront, adjfront);
            if (checksubfaces) {
              tspivot(oldfront, checksh);
              if (checksh.sh != dummysh) {
                tsbond(oldfront, checksh);
              }
            }
          }
          // Delete the new tets.
          tetrahedrondealloc(newtetlist[0].tet);
          tetrahedrondealloc(newtetlist[1].tet);
          // If tmpkey has been modified, then the failure was not due to
          //   unflipable configuration, but the non-improvement.
          if (key && (tmpkey < *key)) {
            *key = tmpkey;
            return false;
          }
        } // if (success)
      } // if (doflip)
    } // if (ori > 0.0)
  } // for (i = 0; i < n; i++)

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebycombNM()    Remove an edge by combining two flipNMs.          //
//                                                                           //
// Given a set T of tets surrounding edge ab. The premise is that ab can not //
// be removed by a flipNM. This routine attempts to remove ab by two flipNMs,//
// i.e., first find and flip an edge af (or bf) by flipNM, then flip ab by   //
// flipNM. If it succeeds, two sets T(ab) and T(af) of tets are replaced by  //
// a new set T' and both ab and af are not edges in T' anymore.              //
//                                                                           //
// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, such that a lies in front of and b lies behind it. Let the //
// projections of the n apexes on the screen in clockwise order are: p_0,...,//
// p_n-1, respectively. So the list of tets are: [0]abp_0p_n-1, [1]abp_1p_0, //
// ..., [n-1]abp_n-1p_n-2, respectively.                                     //
//                                                                           //
// The principle of the approach is: for a face a.b.p_0, check if edge b.p_0 //
// is of type N32 (or N44). If it is, then try to do a flipNM on it. If the  //
// flip is successful, then try to do another flipNM on a.b.  If one of the  //
// two flipNMs fails, restore the old tets as they have never been flipped.  //
// Then try the next face a.b.p_1.  The process can be looped for all faces  //
// having ab. Stop if ab is removed or all faces have been visited. Note in  //
// the above description only b.p_0 is considered, a.p_0 is done by swapping //
// the position of a and b.                                                  //
//                                                                           //
// Similar operations have been described in [Joe,1995].  My approach checks //
// more cases for finding flips than Joe's.  For instance, the cases (1)-(7) //
// of Joe only consider abf for finding a flip (T23/T32).  My approach looks //
// all faces at ab for finding flips. Moreover, the flipNM can flip an edge  //
// whose star may have more than 3 tets while Joe's only works on 3-tet case.//
//                                                                           //
// If ab is removed, 'newtetlist' contains the new tets. Two sets 'abtetlist'//
// (n tets) and 'bftetlist' (n1 tets) have been replaced.  The number of new //
// tets can be calculated by follows: the 1st flip transforms n1 tets into   //
// (n1 - 2) * 2 new tets, however,one of the new tets goes into the new link //
// of ab, i.e., the reduced tet number in Star(ab) is n - 1;  the 2nd flip   //
// transforms n - 1 tets into (n - 3) * 2 new tets. Hence the number of new  //
// tets are: m = ((n1 - 2) * 2 - 1) + (n - 3) * 2.  The old tets are NOT del-//
// eted. The caller has the right to delete them or reverse the operation.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist,
  int *n1, triface *bftetlist, triface *newtetlist, queue *flipque)
{
  triface tmpabtetlist[11];
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, p[10];
  REAL ori, tmpkey, tmpkey2;
  REAL attrib, volume;
  bool doflip, success;
  int twice, count;
  int i, j, k, m;

  // Maximal 10 tets in Star(ab).
  assert(n <= 10);

  // Do the following procedure twice, one for flipping edge b.p_0 and the
  //   other for p_0.a which is symmetric to the first.
  twice = 0;
  do {
    // Two points a and b are fixed.
    pa = org(abtetlist[0]);
    pb = dest(abtetlist[0]);
    // The points p_0, ..., p_n-1 are permuted in the following loop.
    for (i = 0; i < n; i++) {
      // Get the n points for the current configuration.
      for (j = 0; j < n; j++) {
        p[j] = apex(abtetlist[(i + j) % n]);
      }
      // Check if b.p_0 is of type N32 or N44.
      ori = orient3d(pb, p[0], p[1], p[n - 1]);
      if ((ori > 0) && (key != (REAL *) NULL)) {
        // b.p_0 is not N32. However, it is possible that the tet b.p_0.p_1.
        //   p_n-1 has worse quality value than the key. In such case, also
        //   try to flip b.p_0.
        tetalldihedral(pb, p[0], p[n - 1], p[1], NULL, &tmpkey, NULL);
        if (tmpkey < *key) ori = 0.0;
      }
      if (ori <= 0.0) {
        // b.p_0 is either N32 or N44. Try the 1st flipNM.
        bftetlist[0] = abtetlist[i];
        enextself(bftetlist[0]);// go to edge b.p_0.
        adjustedgering(bftetlist[0], CW); // edge p_0.b.
        assert(apex(bftetlist[0]) == pa);
        // Form Star(b.p_0).
        doflip = true;
        *n1 = 0;
        do {
          // Is the list full?
          if (*n1 == 10) break;
          if (checksubfaces) {
            // Stop if a subface appears.
            tspivot(bftetlist[*n1], checksh);
            if (checksh.sh != dummysh) {
              doflip = false; break;
            }
          }
          // Get the next tet at p_0.b.
          fnext(bftetlist[*n1], bftetlist[(*n1) + 1]);
          (*n1)++;
        } while (apex(bftetlist[*n1]) != pa);
        // 2 <= n1 <= 10.
        if (doflip) {
          success = false;
          tmpkey = -1.0;  // = acos(pi).
          if (key != (REAL *) NULL) tmpkey = *key;
          m = 0;
          if (*n1 == 3) {
            // Three tets case. Try flip32.
            success = removeedgebyflip32(&tmpkey,bftetlist,newtetlist,flipque);
            m = 2;
          } else if ((*n1 > 3) && (*n1 < 7)) {
            // Four or more tets case. Try flipNM.
            success = removeedgebytranNM(&tmpkey, *n1, bftetlist, newtetlist,
                                         p[1], p[n - 1], flipque);
            // If success, the number of new tets.
            m = ((*n1) - 2) * 2;
          } else {
            if (b->verbose > 1) {
              printf("  !! Unhandled case: n1 = %d.\n", *n1);
            }
          }
          if (success) {
            // b.p_0 is flipped. The link of ab is reduced (by 1), i.e., p_0
            //   is not on the link of ab. Two old tets a.b.p_0.p_n-1 and
            //   a.b.p_1.p_0 have been removed from the Star(ab) and one new
            //   tet t = a.b.p_1.p_n-1 belongs to Star(ab). 
            // Find t in the 'newtetlist' and remove it from the list.
            setpointmark(pa, -pointmark(pa) - 1);
            setpointmark(pb, -pointmark(pb) - 1);
            assert(m > 0);
            for (j = 0; j < m; j++) {
              tmpabtetlist[0] = newtetlist[j];
              // Does it has ab?
              count = 0;
              for (k = 0; k < 4; k++) {
                if (pointmark((point)(tmpabtetlist[0].tet[4+k])) < 0) count++;
              }
              if (count == 2) {
                // It is. Adjust t to be the edge ab.
                for (tmpabtetlist[0].loc = 0; tmpabtetlist[0].loc < 4;
                     tmpabtetlist[0].loc++) {
                  if ((oppo(tmpabtetlist[0]) != pa) &&
                      (oppo(tmpabtetlist[0]) != pb)) break;
                }
                // The face of t must contain ab.
                assert(tmpabtetlist[0].loc < 4);
                findedge(&(tmpabtetlist[0]), pa, pb);
                break;
              }
            }
            assert(j < m); // The tet must exist.
            // Remove t from list. Fill t's position by the last tet.
            newtetlist[j] = newtetlist[m - 1];
            setpointmark(pa, -(pointmark(pa) + 1));
            setpointmark(pb, -(pointmark(pb) + 1));
            // Create the temporary Star(ab) for the next flipNM.
            adjustedgering(tmpabtetlist[0], CCW);
            if (org(tmpabtetlist[0]) != pa) {
              fnextself(tmpabtetlist[0]);
              esymself(tmpabtetlist[0]);
            }
#ifdef SELF_CHECK
            // Make sure current edge is a->b.
            assert(org(tmpabtetlist[0]) == pa);
            assert(dest(tmpabtetlist[0]) == pb);
            assert(apex(tmpabtetlist[0]) == p[n - 1]);
            assert(oppo(tmpabtetlist[0]) == p[1]);
#endif // SELF_CHECK
            // There are n - 2 left temporary tets.
            for (j = 1; j < n - 1; j++) {
              maketetrahedron(&(tmpabtetlist[j]));
              setorg(tmpabtetlist[j], pa);
              setdest(tmpabtetlist[j], pb);
              setapex(tmpabtetlist[j], p[j]);
              setoppo(tmpabtetlist[j], p[j + 1]);
            }
            // Transfer the element attributes.
            for (j = 0; j < in->numberoftetrahedronattributes; j++) {
              attrib = elemattribute(abtetlist[0].tet, j);
              for (k = 0; k < n - 1; k++) {
                setelemattribute(tmpabtetlist[k].tet, j, attrib);
              }
            }
            // Transfer the volume constraints.
            if (b->varvolume && !b->refine) {
              volume = volumebound(abtetlist[0].tet);
              for (k = 0; k < n - 1; k++) {
                setvolumebound(tmpabtetlist[k].tet, volume);
              }
            }
            // Glue n - 1 internal faces of Star(ab).
            for (j = 0; j < n - 1; j++) {
              fnext(tmpabtetlist[j], newfront);
              bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1
            }
            // Substitute the old tets with the new tets by connecting the
            //   new tets to the adjacent tets in the mesh. There are (n-2)
            //   * 2 (outer) faces of the new tets need to be operated.
            // Note that the old tets still have the pointers to their
            //   adjacent tets in the mesh.  These pointers can be re-used
            //   to inverse the substitution.
            for (j = 2; j < n; j++) {
              // Get an old tet: [j]a.b.p_j.p_j-1, (j > 1).
              oldfront = abtetlist[(i + j) % n];
              esymself(oldfront);
              enextfnextself(oldfront);
              // Get an adjacent tet at face: [j]a.p_j.p_j-1.
              sym(oldfront, adjfront); // adjfront may be dummy.
              // Get the corresponding face from the new tets.
              // j >= 2.
              enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1
              bond(newfront, adjfront);
              if (checksubfaces) {
                tspivot(oldfront, checksh); 
                if (checksh.sh != dummysh) {
                  tsbond(newfront, checksh);
                }
              }
            }
            for (j = 2; j < n; j++) {
              // Get an old tet: [j]a.b.p_j.p_j-1, (j > 2).
              oldfront = abtetlist[(i + j) % n];
              esymself(oldfront);
              enext2fnextself(oldfront);
              // Get an adjacent tet at face: [j]b.p_j.p_j-1.
              sym(oldfront, adjfront); // adjfront may be dummy.
              // Get the corresponding face from the new tets.
              // j >= 2.
              enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1
              bond(newfront, adjfront);
              if (checksubfaces) {
                tspivot(oldfront, checksh); 
                if (checksh.sh != dummysh) {
                  tsbond(newfront, checksh);
                }
              }
            }
            // Adjust the faces in the temporary new tets at ab for
            //   recursively processing on the n-1 tets.
            for (j = 0; j < n - 1; j++) {
              fnextself(tmpabtetlist[j]);
            }
            tmpkey2 = -1;
            if (key) tmpkey2 = *key;
            if ((n - 1) == 3) {
              success = removeedgebyflip32(&tmpkey2, tmpabtetlist,
                &(newtetlist[m - 1]), flipque);
            } else { // assert((n - 1) >= 4);
              success = removeedgebytranNM(&tmpkey2, n - 1, tmpabtetlist,
                &(newtetlist[m - 1]), NULL, NULL, flipque);
            }
            // No matter it was success or not, delete the temporary tets.
            for (j = 0; j < n - 1; j++) {
              tetrahedrondealloc(tmpabtetlist[j].tet);
            }
            if (success) {
              // The new configuration is good. 
              // Do not delete the old tets.
              // for (j = 0; j < n; j++) {
              //   tetrahedrondealloc(abtetlist[j].tet);
              // }
              // Return the bigger dihedral in the two sets of new tets.
              if (key != (REAL *) NULL) {
                *key = tmpkey2 < tmpkey ? tmpkey2 : tmpkey;
              }
              return true;
            } else {
              // The new configuration is bad, substitue back the old tets.
              for (j = 0; j < n; j++) {
                oldfront = abtetlist[(i + j) % n];
                esymself(oldfront);
                enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy.
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              for (j = 0; j < n; j++) {
                oldfront = abtetlist[(i + j) % n];
                esymself(oldfront);
                enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              // Substitute back the old tets of the first flip.
              for (j = 0; j < *n1; j++) {
                oldfront = bftetlist[j];
                esymself(oldfront);
                enextfnextself(oldfront);
                sym(oldfront, adjfront); // adjfront may be dummy.
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              for (j = 0; j < *n1; j++) {
                oldfront = bftetlist[j];
                esymself(oldfront);
                enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              // Delete the new tets of the first flip. Note that one new
              //   tet has already been removed from the list.
              for (j = 0; j < m - 1; j++) {
                tetrahedrondealloc(newtetlist[j].tet);
              }
            } // if (success)
          } // if (success)
        } // if (doflip)
      } // if (ori <= 0.0)
    } // for (i = 0; i < n; i++)
    // Inverse a and b and the tets configuration.
    for (i = 0; i < n; i++) newtetlist[i] = abtetlist[i];
    for (i = 0; i < n; i++) {
      oldfront = newtetlist[n - i - 1];
      esymself(oldfront);
      fnextself(oldfront);
      abtetlist[i] = oldfront;
    }
    twice++;
  } while (twice < 2);

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetrahedron()    Insert a point into a tetrahedron, split it into    //
//                       four tetrahedra.                                    //
//                                                                           //
// The tetrahedron is given by 'splittet'.  Let it is abcd.  The inserting   //
// point 'newpoint' v should lie strictly inside abcd.                       //
//                                                                           //
// Splitting a tetrahedron is to shrink abcd to abcv,  and create three new  //
// tetrahedra badv, cbdv, and acdv.                                          //
//                                                                           //
// On completion, 'splittet' returns abcv.  If 'flipqueue' is not NULL, it   //
// contains all possibly non-locally Delaunay faces.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splittetrahedron(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface oldabd, oldbcd, oldcad;                      // Old configuration.
  triface abdcasing, bcdcasing, cadcasing;
  face abdsh, bcdsh, cadsh;
  triface abcv, badv, cbdv, acdv;                      // New configuration.
  triface worktet;
  face abseg, bcseg, caseg;
  face adseg, bdseg, cdseg;
  point pa, pb, pc, pd;
  REAL attrib, volume;
  int i;

  abcv = *splittet;
  abcv.ver = 0;
  // Set the changed vertices and new tetrahedron.
  pa = org(abcv);
  pb = dest(abcv);
  pc = apex(abcv);
  pd = oppo(abcv);

  if (b->verbose > 1) {
    printf("  Inserting point %d in tetrahedron (%d, %d, %d, %d).\n",
           pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc),
           pointmark(pd));
  }

  fnext(abcv, oldabd);
  enextfnext(abcv, oldbcd);
  enext2fnext(abcv, oldcad);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  maketetrahedron(&badv);
  maketetrahedron(&cbdv);
  maketetrahedron(&acdv);

  // Set 'badv' vertices.
  setorg (badv, pb);
  setdest(badv, pa);
  setapex(badv, pd);
  setoppo(badv, newpoint);
  // Set 'cbdv' vertices.
  setorg (cbdv, pc);
  setdest(cbdv, pb);
  setapex(cbdv, pd);
  setoppo(cbdv, newpoint);
  // Set 'acdv' vertices.
  setorg (acdv, pa);
  setdest(acdv, pc);
  setapex(acdv, pd);
  setoppo(acdv, newpoint);
  // Set 'abcv' vertices
  setoppo(abcv, newpoint);

  // Set the element attributes of the new tetrahedra.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abcv.tet, i);
    setelemattribute(badv.tet, i, attrib);
    setelemattribute(cbdv.tet, i, attrib);
    setelemattribute(acdv.tet, i, attrib);
  }
  // Set the volume constraint of the new tetrahedra.
  if (b->varvolume) {
    volume = volumebound(abcv.tet);
    setvolumebound(badv.tet, volume);
    setvolumebound(cbdv.tet, volume);
    setvolumebound(acdv.tet, volume);
  }

  // Bond the new triangles to the surrounding tetrahedron.
  bond(badv, abdcasing);
  bond(cbdv, bcdcasing);
  bond(acdv, cadcasing);
  // There may exist subfaces need to be bonded to the new tetrahedra.
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    if (abdsh.sh != dummysh) {
      tsdissolve(oldabd);
      tsbond(badv, abdsh);
    }
    tspivot(oldbcd, bcdsh);
    if (bcdsh.sh != dummysh) {
      tsdissolve(oldbcd);
      tsbond(cbdv, bcdsh);
    }
    tspivot(oldcad, cadsh);
    if (cadsh.sh != dummysh) {
      tsdissolve(oldcad);
      tsbond(acdv, cadsh);
    }
  } else if (checksubsegs) {
    tsspivot1(abcv, abseg);
    if (abseg.sh != dummysh) {
      tssbond1(badv, abseg);
    }
    enext(abcv, worktet);
    tsspivot1(worktet, bcseg);
    if (bcseg.sh != dummysh) {
      tssbond1(cbdv, bcseg);
    }
    enext2(abcv, worktet);
    tsspivot1(worktet, caseg);
    if (caseg.sh != dummysh) {
      tssbond1(acdv, caseg);
    }
    fnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, adseg);
    if (adseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(badv, worktet);
      tssbond1(worktet, adseg);
      enext2(acdv, worktet);
      tssbond1(worktet, adseg);
    }
    enextfnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, bdseg);
    if (bdseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(cbdv, worktet);
      tssbond1(worktet, bdseg);
      enext2(badv, worktet);
      tssbond1(worktet, bdseg);
    }
    enext2fnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, cdseg);
    if (cdseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(acdv, worktet);
      tssbond1(worktet, cdseg);
      enext2(cbdv, worktet);
      tssbond1(worktet, cdseg);
    }
  }
  badv.loc = 3; 
  cbdv.loc = 2;
  bond(badv, cbdv);
  cbdv.loc = 3; 
  acdv.loc = 2;
  bond(cbdv, acdv);
  acdv.loc = 3; 
  badv.loc = 2;
  bond(acdv, badv);
  badv.loc = 1; 
  bond(badv, oldabd);
  cbdv.loc = 1; 
  bond(cbdv, oldbcd);
  acdv.loc = 1; 
  bond(acdv, oldcad);
  
  badv.loc = 0;
  cbdv.loc = 0;
  acdv.loc = 0;
  if (b->verbose > 3) {
    printf("    Updating abcv ");
    printtet(&abcv);
    printf("    Creating badv ");
    printtet(&badv);
    printf("    Creating cbdv ");
    printtet(&cbdv);
    printf("    Creating acdv ");
    printtet(&acdv);
  }

  if (flipqueue != (queue *) NULL) {
    enqueueflipface(abcv, flipqueue);
    enqueueflipface(badv, flipqueue);
    enqueueflipface(cbdv, flipqueue);
    enqueueflipface(acdv, flipqueue);
  }

  // Save a handle for quick point location.
  recenttet = abcv;
  // Set the return handle be abcv.
  *splittet = abcv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unsplittetrahedron()    Reverse the operation of inserting a point into a //
//                         tetrahedron, so as to remove the newly inserted   //
//                         point from the mesh.                              //
//                                                                           //
// Assume the origional tetrahedron is abcd, it was split by v into four     //
// tetrahedra abcv, badv, cbdv, and acdv. 'splittet' represents face abc of  //
// abcv (i.e., its opposite is v).                                           //
//                                                                           //
// Point v is removed by expanding abcv to abcd, deleting three tetrahedra   //
// badv, cbdv and acdv.  On return, point v is not deleted in this routine.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unsplittetrahedron(triface* splittet)
{
  triface abcv, badv, cbdv, acdv;
  triface oldabv, oldbcv, oldcav;
  triface badcasing, cbdcasing, acdcasing;
  face badsh, cbdsh, acdsh;

  abcv = *splittet;
  adjustedgering(abcv, CCW);  // for sure.
  fnext(abcv, oldabv);
  fnext(oldabv, badv);
  esymself(badv);
  enextfnext(abcv, oldbcv);
  fnext(oldbcv, cbdv);
  esymself(cbdv);
  enext2fnext(abcv, oldcav);
  fnext(oldcav, acdv);
  esymself(acdv);

  if (b->verbose > 1) {
    printf("  Removing point %d in tetrahedron (%d, %d, %d, %d).\n",
           pointmark(oppo(abcv)), pointmark(org(abcv)), pointmark(dest(abcv)),
           pointmark(apex(abcv)), pointmark(apex(badv)));
  }

  sym(badv, badcasing);
  tspivot(badv, badsh);
  sym(cbdv, cbdcasing);
  tspivot(cbdv, cbdsh);
  sym(acdv, acdcasing);
  tspivot(acdv, acdsh);

  // Expanding abcv to abcd.
  setoppo(abcv, apex(badv));
  bond(oldabv, badcasing);
  if (badsh.sh != dummysh) {
    tsbond(oldabv, badsh);
  }
  bond(oldbcv, cbdcasing);
  if (cbdsh.sh != dummysh) {
    tsbond(oldbcv, cbdsh);
  }
  bond(oldcav, acdcasing);
  if (acdsh.sh != dummysh) {
    tsbond(oldcav, acdsh);
  }

  // Delete the three split-out tetrahedra.
  tetrahedrondealloc(badv.tet);
  tetrahedrondealloc(cbdv.tet);
  tetrahedrondealloc(acdv.tet);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetface()    Insert a point on a face of a mesh.                     //
//                                                                           //
// 'splittet' is the splitting face.  Let it is abcd, where abc is the face  //
// will be split. If abc is not a hull face, abce is the tetrahedron at the  //
// opposite of d.                                                            //
//                                                                           //
// To split face abc by a point v is to shrink the tetrahedra abcd to abvd,  //
// create two new tetrahedra bcvd, cavd.  If abc is not a hull face, shrink  //
// the tetrahedra bace to bave, create two new tetrahedra cbve, acve.        //
//                                                                           //
// If abc is a subface, it is split into three subfaces simultaneously by    //
// calling routine splitsubface(), hence, abv, bcv, cav.  The edge rings of  //
// the split subfaces have the same orientation as abc's.                    //
//                                                                           //
// On completion, 'splittet' returns abvd.  If 'flipqueue' is not NULL, it   //
// contains all possibly non-locally Delaunay faces.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splittetface(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface abcd, bace;                                  // Old configuration.
  triface oldbcd, oldcad, oldace, oldcbe; 
  triface bcdcasing, cadcasing, acecasing, cbecasing;
  face abcsh, bcdsh, cadsh, acesh, cbesh;
  triface abvd, bcvd, cavd, bave, cbve, acve;          // New configuration.
  triface worktet;
  face bcseg, caseg;
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  bool mirrorflag;
  int i;

  abcd = *splittet;
  // abcd.ver = 0; // Adjust to be CCW edge ring.
  adjustedgering(abcd, CCW);
  pa = org(abcd);
  pb = dest(abcd);
  pc = apex(abcd);
  pd = oppo(abcd);
  pe = (point) NULL; // avoid a compile warning.
  // Is there a second tetrahderon?
  mirrorflag = issymexist(&abcd);
  if (mirrorflag) {
    // This is an interior face.
    sym(abcd, bace);
    findedge(&bace, dest(abcd), org(abcd));
    pe = oppo(bace);
  }
  if (checksubfaces) {
    // Is there a subface need to be split together?
    tspivot(abcd, abcsh);
    if (abcsh.sh != dummysh) {
      // Exists! Keep the edge ab of both handles be the same.
      findedge(&abcsh, org(abcd), dest(abcd));
    }
  }

  if (b->verbose > 1) {
    printf("  Inserting point %d on face (%d, %d, %d).\n", pointmark(newpoint),
           pointmark(pa), pointmark(pb), pointmark(pc));
  }

  // Save the old configuration at faces bcd and cad.
  enextfnext(abcd, oldbcd);
  enext2fnext(abcd, oldcad);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  // Create two new tetrahedra.
  maketetrahedron(&bcvd);
  maketetrahedron(&cavd);
  if (mirrorflag) {
    // Save the old configuration at faces bce and cae.
    enextfnext(bace, oldace);
    enext2fnext(bace, oldcbe);
    sym(oldace, acecasing);
    sym(oldcbe, cbecasing);
    // Create two new tetrahedra.
    maketetrahedron(&acve);
    maketetrahedron(&cbve);
  } else {
    // Splitting a boundary face increases the number of boundary faces.
    hullsize += 2;
  }

  // Set vertices to the changed tetrahedron and new tetrahedra.
  abvd = abcd;  // Update 'abcd' to 'abvd'.
  setapex(abvd, newpoint);
  setorg (bcvd, pb);  // Set 'bcvd'.
  setdest(bcvd, pc);
  setapex(bcvd, newpoint);
  setoppo(bcvd, pd);
  setorg (cavd, pc);  // Set 'cavd'.
  setdest(cavd, pa);
  setapex(cavd, newpoint);
  setoppo(cavd, pd);
  // Set the element attributes of the new tetrahedra.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abvd.tet, i);
    setelemattribute(bcvd.tet, i, attrib);
    setelemattribute(cavd.tet, i, attrib);
  }
  if (b->varvolume) {
    // Set the area constraint of the new tetrahedra.
    volume = volumebound(abvd.tet);
    setvolumebound(bcvd.tet, volume);
    setvolumebound(cavd.tet, volume);
  }
  if (mirrorflag) {
    bave = bace;  // Update 'bace' to 'bave'.
    setapex(bave, newpoint);
    setorg (acve, pa);  // Set 'acve'.
    setdest(acve, pc);
    setapex(acve, newpoint);
    setoppo(acve, pe);
    setorg (cbve, pc);  // Set 'cbve'.
    setdest(cbve, pb);
    setapex(cbve, newpoint);
    setoppo(cbve, pe);
    // Set the element attributes of the new tetrahedra.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(bave.tet, i);
      setelemattribute(acve.tet, i, attrib);
      setelemattribute(cbve.tet, i, attrib);
    }
    if (b->varvolume) {
      // Set the area constraint of the new tetrahedra.
      volume = volumebound(bave.tet);
      setvolumebound(acve.tet, volume);
      setvolumebound(cbve.tet, volume);
    }
  }

  // Bond the new tetrahedra to the surrounding tetrahedra.
  bcvd.loc = 1;
  bond(bcvd, bcdcasing); 
  cavd.loc = 1;
  bond(cavd, cadcasing); 
  bcvd.loc = 3;
  bond(bcvd, oldbcd);
  cavd.loc = 2;
  bond(cavd, oldcad);
  bcvd.loc = 2;
  cavd.loc = 3;
  bond(bcvd, cavd);  
  if (mirrorflag) {
    acve.loc = 1;
    bond(acve, acecasing);
    cbve.loc = 1;
    bond(cbve, cbecasing);
    acve.loc = 3;
    bond(acve, oldace);
    cbve.loc = 2;
    bond(cbve, oldcbe);
    acve.loc = 2;
    cbve.loc = 3;
    bond(acve, cbve);
    // Bond two new coplanar facets.
    bcvd.loc = 0;
    cbve.loc = 0;
    bond(bcvd, cbve);
    cavd.loc = 0;
    acve.loc = 0;
    bond(cavd, acve);
  }

  // There may exist subface needed to be bonded to the new tetrahedra.
  if (checksubfaces) {
    tspivot(oldbcd, bcdsh);
    if (bcdsh.sh != dummysh) {
      tsdissolve(oldbcd);
      bcvd.loc = 1;
      tsbond(bcvd, bcdsh);
    }
    tspivot(oldcad, cadsh);
    if (cadsh.sh != dummysh) {
      tsdissolve(oldcad);
      cavd.loc = 1;
      tsbond(cavd, cadsh);
    }
    if (mirrorflag) {
      tspivot(oldace, acesh);
      if (acesh.sh != dummysh) {
        tsdissolve(oldace);
        acve.loc = 1;
        tsbond(acve, acesh);
      }
      tspivot(oldcbe, cbesh);
      if (cbesh.sh != dummysh) {
        tsdissolve(oldcbe);
        cbve.loc = 1;
        tsbond(cbve, cbesh);
      }
    }
    // Is there a subface needs to be split together?
    if (abcsh.sh != dummysh) {
      // Split this subface 'abc' into three i.e, abv, bcv, cav.
      splitsubface(newpoint, &abcsh, (queue *) NULL);
    }  
  } else if (checksubsegs) {
    // abvd.loc = abvd.ver = 0;
    bcvd.loc = bcvd.ver = 0;
    cavd.loc = cavd.ver = 0;
    if (mirrorflag) {
      // bave.loc = bave.ver = 0;
      cbve.loc = cbve.ver = 0;
      acve.loc = acve.ver = 0;
    }
    enext(abvd, worktet);
    tsspivot1(worktet, bcseg);
    if (bcseg.sh != dummysh) {
      tssdissolve1(worktet);
      tssbond1(bcvd, bcseg);
      if (mirrorflag) {
        enext2(bave, worktet);
        tssdissolve1(worktet);
        tssbond1(cbve, bcseg);
      }
    }
    enext2(abvd, worktet);
    tsspivot1(worktet, caseg);
    if (caseg.sh != dummysh) {
      tssdissolve1(worktet);
      tssbond1(cavd, caseg);
      if (mirrorflag) {
        enext(bave, worktet);
        tssdissolve1(worktet);
        tssbond1(acve, caseg);
      }
    }
    fnext(abvd, worktet);
    enext2self(worktet);
    tsspivot1(worktet, adseg);
    if (adseg.sh != dummysh) {
      fnext(cavd, worktet);
      enextself(worktet);
      tssbond1(worktet, adseg);
    }
    fnext(abvd, worktet);
    enextself(worktet);
    tsspivot1(worktet, bdseg);
    if (bdseg.sh != dummysh) {
      fnext(bcvd, worktet);
      enext2self(worktet);
      tssbond1(worktet, bdseg);
    }
    enextfnext(abvd, worktet);
    enextself(worktet);
    tsspivot1(worktet, cdseg);
    if (cdseg.sh != dummysh) {
      tssdissolve1(worktet);
      fnext(bcvd, worktet);
      enextself(worktet);
      tssbond1(worktet, cdseg);
      fnext(cavd, worktet);
      enext2self(worktet);
      tssbond1(worktet, cdseg);
    }
    if (mirrorflag) {
      fnext(bave, worktet);
      enextself(worktet);
      tsspivot1(worktet, aeseg);
      if (aeseg.sh != dummysh) {
        fnext(acve, worktet);
        enext2self(worktet);
        tssbond1(worktet, aeseg);
      }
      fnext(bave, worktet);
      enext2self(worktet);
      tsspivot1(worktet, beseg);
      if (beseg.sh != dummysh) {
        fnext(cbve, worktet);
        enextself(worktet);
        tssbond1(worktet, beseg);
      }
      enextfnext(bave, worktet);
      enextself(worktet);
      tsspivot1(worktet, ceseg);
      if (ceseg.sh != dummysh) {
        tssdissolve1(worktet);
        fnext(cbve, worktet);
        enext2self(worktet);
        tssbond1(worktet, ceseg);
        fnext(acve, worktet);
        enextself(worktet);
        tssbond1(worktet, ceseg);
      }
    }
  }

  // Save a handle for quick point location.
  recenttet = abvd;
  // Set the return handle be abvd.
  *splittet = abvd;

  bcvd.loc = 0;
  cavd.loc = 0;
  if (mirrorflag) {
    cbve.loc = 0;
    acve.loc = 0;
  }
  if (b->verbose > 3) {
    printf("    Updating abvd ");
    printtet(&abvd);
    printf("    Creating bcvd ");
    printtet(&bcvd);
    printf("    Creating cavd ");
    printtet(&cavd);
    if (mirrorflag) {
      printf("    Updating bave ");
      printtet(&bave);
      printf("    Creating cbve ");
      printtet(&cbve);
      printf("    Creating acve ");
      printtet(&acve);
    }
  }

  if (flipqueue != (queue *) NULL) {
    fnextself(abvd);
    enqueueflipface(abvd, flipqueue);
    fnextself(bcvd);
    enqueueflipface(bcvd, flipqueue);
    fnextself(cavd);
    enqueueflipface(cavd, flipqueue);
    if (mirrorflag) {
      fnextself(bave);
      enqueueflipface(bave, flipqueue);
      fnextself(cbve);
      enqueueflipface(cbve, flipqueue);
      fnextself(acve);
      enqueueflipface(acve, flipqueue);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unsplittetface()    Reverse the operation of inserting a point on a face, //
//                     so as to remove the newly inserted point.             //
//                                                                           //
// Assume the original face is abc, the tetrahedron containing abc is abcd.  //
// If abc is not a hull face, bace is the tetrahedron at the opposite of d.  //
// After face abc was split by a point v, tetrahedron abcd had been split    //
// into three tetrahedra, abvd, bcvd, cavd, and bace (if it exists) had been //
// split into bave, cbve, acve. 'splittet' represents abvd (its apex is v).  //
//                                                                           //
// Point v is removed by expanding abvd to abcd, deleting two tetrahedra     //
// bcvd, cavd. Expanding bave(if it exists) to bace, deleting two tetrahedra //
// cbve, acve.  If abv is a subface, routine unsplitsubface() will be called //
// to reverse the operation of splitting a subface. On completion, point v   //
// is not deleted in this routine.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unsplittetface(triface* splittet)
{
  triface abvd, bcvd, cavd, bave, cbve, acve;
  triface oldbvd, oldvad, oldvbe, oldave;
  triface bcdcasing, cadcasing, cbecasing, acecasing;
  face bcdsh, cadsh, cbesh, acesh;
  face abvsh;
  bool mirrorflag;

  abvd = *splittet;
  adjustedgering(abvd, CCW); // for sure.
  enextfnext(abvd, oldbvd);
  fnext(oldbvd, bcvd);
  esymself(bcvd);
  enextself(bcvd);
  enext2fnext(abvd, oldvad);
  fnext(oldvad, cavd);
  esymself(cavd);
  enext2self(cavd);
  // Is there a second tetrahedron?
  sym(abvd, bave);
  mirrorflag = bave.tet != dummytet;
  if (mirrorflag) {
    findedge(&bave, dest(abvd), org(abvd));
    enextfnext(bave, oldave);  
    fnext(oldave, acve);
    esymself(acve);
    enextself(acve);
    enext2fnext(bave, oldvbe);  
    fnext(oldvbe, cbve);
    esymself(cbve);
    enext2self(cbve);
  } else {
    // Unsplit a hull face decrease the number of boundary faces.
    hullsize -= 2;
  }
  // Is there a subface at abv.
  tspivot(abvd, abvsh);
  if (abvsh.sh != dummysh) {
    // Exists! Keep the edge ab of both handles be the same.
    findedge(&abvsh, org(abvd), dest(abvd));
  }

  if (b->verbose > 1) {
    printf("  Removing point %d on face (%d, %d, %d).\n",
           pointmark(apex(abvd)), pointmark(org(abvd)), pointmark(dest(abvd)),
           pointmark(dest(bcvd)));
  }

  fnextself(bcvd); // bcvd has changed to bcdv.
  sym(bcvd, bcdcasing);
  tspivot(bcvd, bcdsh);
  fnextself(cavd); // cavd has changed to cadv.
  sym(cavd, cadcasing);
  tspivot(cavd, cadsh);
  if (mirrorflag) {
    fnextself(acve); // acve has changed to acev.
    sym(acve, acecasing);
    tspivot(acve, acesh);
    fnextself(cbve); // cbve has changed to cbev.
    sym(cbve, cbecasing);
    tspivot(cbve, cbesh);
  }

  // Expand abvd to abcd.
  setapex(abvd, dest(bcvd));
  bond(oldbvd, bcdcasing);
  if (bcdsh.sh != dummysh) {
    tsbond(oldbvd, bcdsh);
  }
  bond(oldvad, cadcasing);
  if (cadsh.sh != dummysh) {
    tsbond(oldvad, cadsh);
  }
  if (mirrorflag) {
    // Expanding bave to bace.
    setapex(bave, dest(acve));
    bond(oldave, acecasing);
    if (acesh.sh != dummysh) {
      tsbond(oldave, acesh);
    }
    bond(oldvbe, cbecasing);
    if (cbesh.sh != dummysh) {
      tsbond(oldvbe, cbesh);
    }
  }

  // Unsplit a subface if there exists.
  if (abvsh.sh != dummysh) {
    unsplitsubface(&abvsh);
  }

  // Delete the split-out tetrahedra.
  tetrahedrondealloc(bcvd.tet);
  tetrahedrondealloc(cavd.tet);
  if (mirrorflag) {
    tetrahedrondealloc(acve.tet);
    tetrahedrondealloc(cbve.tet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubface()    Insert a point on a subface, split it into three.       //
//                                                                           //
// The subface is 'splitface'.  Let it is abc. The inserting point 'newpoint'//
// v should lie inside abc.  If the neighbor tetrahedra of abc exist, i.e.,  //
// abcd and bace, they should have been split by routine splittetface()      //
// before calling this routine, so the connection between the new tetrahedra //
// and new subfaces can be correctly set.                                    //
//                                                                           //
// To split subface abc by point v is to shrink abc to abv, create two new   //
// subfaces bcv and cav.  Set the connection between updated and new created //
// subfaces. If there is a subsegment at edge bc or ca, connection of new    //
// subface (bcv or cav) to its casing subfaces is a face link, 'casingin' is //
// the predecessor and 'casingout' is the successor. It is important to keep //
// the orientations of the edge rings of the updated and created subfaces be //
// the same as abc's. So they have the same orientation as other subfaces of //
// this facet with respect to the lift point of this facet.                  //
//                                                                           //
// On completion, 'splitface' returns abv.  If 'flipqueue' is not NULL, it   //
// returns all possibly non-Delaunay edges.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitsubface(point newpoint, face* splitface,
  queue* flipqueue)
{
  triface abvd, bcvd, cavd, bave, cbve, acve;
  face abc, oldbc, oldca, bc, ca, spinsh;
  face bccasin, bccasout, cacasin, cacasout;
  face abv, bcv, cav;
  point pa, pb, pc;
  
  abc = *splitface;
  // The newly created subfaces will have the same edge ring as abc.
  adjustedgering(abc, CCW);
  pa = sorg(abc);
  pb = sdest(abc);
  pc = sapex(abc);

  if (b->verbose > 1) {
    printf("  Inserting point %d on subface (%d, %d, %d).\n",
           pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc));
  }

  // Save the old configuration at edge bc and ca.  Subsegments may appear
  //   at both sides, save the face links and dissolve them.
  senext(abc, oldbc);
  senext2(abc, oldca);
  spivot(oldbc, bccasout);
  sspivot(oldbc, bc);
  if (bc.sh != dummysh) {
    if (oldbc.sh != bccasout.sh) {
      // 'oldbc' is not self-bonded.
      spinsh = bccasout;
      do {
        bccasin = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != oldbc.sh);
    } else {
      bccasout.sh = dummysh;
    }
    ssdissolve(oldbc);
  } 
  spivot(oldca, cacasout);
  sspivot(oldca, ca);
  if (ca.sh != dummysh) {
    if (oldca.sh != cacasout.sh) {
      // 'oldca' is not self-bonded.
      spinsh = cacasout;
      do {
        cacasin = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != oldca.sh);
    } else {
      cacasout.sh = dummysh;
    }
    ssdissolve(oldca);
  }
  // Create two new subfaces.
  makeshellface(subfaces, &bcv);
  makeshellface(subfaces, &cav);

  // Set the vertices of changed and new subfaces.
  abv = abc;  // Update 'abc' to 'abv'.
  setsapex(abv, newpoint);
  setsorg(bcv, pb);  // Set 'bcv'.
  setsdest(bcv, pc);
  setsapex(bcv, newpoint);
  setsorg(cav, pc);  // Set 'cav'.
  setsdest(cav, pa);
  setsapex(cav, newpoint);
  if (b->quality && varconstraint) {
    // Copy yhr area bound into the new subfaces.
    setareabound(bcv, areabound(abv));
    setareabound(cav, areabound(abv));
  }
  // Copy the boundary mark into the new subfaces.
  setshellmark(bcv, shellmark(abv));
  setshellmark(cav, shellmark(abv));  
  // Copy the subface type into the new subfaces.
  setshelltype(bcv, shelltype(abv));
  setshelltype(cav, shelltype(abv));
  if (checkpbcs) {
    // Copy the pbcgroup into the new subfaces.
    setshellpbcgroup(bcv, shellpbcgroup(abv));
    setshellpbcgroup(cav, shellpbcgroup(abv));
  }
  // Bond the new subfaces to the surrounding subfaces.
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      sbond1(bccasin, bcv);
      sbond1(bcv, bccasout);
    } else {
      // Bond 'bcv' to itsself.
      sbond(bcv, bcv);
    }
    ssbond(bcv, bc);
  } else {
    sbond(bcv, bccasout);
  }
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      sbond1(cacasin, cav);
      sbond1(cav, cacasout);
    } else {
      // Bond 'cav' to itself.
      sbond(cav, cav);
    }
    ssbond(cav, ca);
  } else {
    sbond(cav, cacasout);
  }
  senext2self(bcv);
  sbond(bcv, oldbc);
  senextself(cav);
  sbond(cav, oldca);
  senext2self(bcv);
  senextself(cav);
  sbond(bcv, cav);

  // Bond the new subfaces to the new tetrahedra if they exist.
  stpivot(abv, abvd);
  if (abvd.tet != dummytet) {
    // Get two new tetrahedra and their syms.
    findedge(&abvd, sorg(abv), sdest(abv));
    enextfnext(abvd, bcvd);
#ifdef SELF_CHECK
    assert(bcvd.tet != dummytet);
#endif
    fnextself(bcvd);
    enext2fnext(abvd, cavd);
#ifdef SELF_CHECK
    assert(cavd.tet != dummytet);
#endif
    fnextself(cavd);
    // Bond two new subfaces to the two new tetrahedra.
    tsbond(bcvd, bcv);
    tsbond(cavd, cav);
  }
  // Set the connection at the other sides if the tetrahedra exist.
  sesymself(abv);  // bav
  stpivot(abv, bave);
  if (bave.tet != dummytet) {
    sesymself(bcv);  // cbv
    sesymself(cav);  // acv
    // Get two new tetrahedra and their syms.
    findedge(&bave, sorg(abv), sdest(abv));
    enextfnext(bave, acve);
#ifdef SELF_CHECK
    assert(acve.tet != dummytet);
#endif
    fnextself(acve);
    enext2fnext(bave, cbve);
#ifdef SELF_CHECK
    assert(cbve.tet != dummytet);
#endif
    fnextself(cbve);
    // Bond two new subfaces to the two new tetrahedra.
    tsbond(acve, cav);
    tsbond(cbve, bcv);
  }

  bcv.shver = 0;
  cav.shver = 0;
  if (b->verbose > 3) {
    printf("    Updating abv ");
    printsh(&abv);
    printf("    Creating bcv ");
    printsh(&bcv);
    printf("    Creating cav ");
    printsh(&cav);
  }

  if (flipqueue != (queue *) NULL) {
    enqueueflipedge(abv, flipqueue);
    enqueueflipedge(bcv, flipqueue);
    enqueueflipedge(cav, flipqueue);
  }

  // Set the return handle be abv.
  *splitface = abv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unsplitsubface()    Reverse the operation of inserting a point on a       //
//                     subface, so as to remove the newly inserted point.    //
//                                                                           //
// Assume the original subface is abc, it was split by a point v into three  //
// subfaces abv, bcv and cav.  'splitsh' represents abv.                     //
//                                                                           //
// To remove point v is to expand abv to abc, delete bcv and cav. If edge bc //
// or ca is a subsegment,  the connection at a subsegment is a subface link, //
// '-casin' and '-casout' are used to save the predecessor and successor of  //
// bcv or cav.  On completion, point v is not deleted in this routine.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unsplitsubface(face* splitsh)
{
  face abv, bcv, cav;
  face oldbv, oldva, bc, ca, spinsh;
  face bccasin, bccasout, cacasin, cacasout;

  abv = *splitsh;
  senext(abv, oldbv);
  spivot(oldbv, bcv);
  if (sorg(bcv) != sdest(oldbv)) {
    sesymself(bcv);
  }
  senextself(bcv);
  senext2(abv, oldva);
  spivot(oldva, cav);
  if (sorg(cav) != sdest(oldva)) {
    sesymself(cav);
  }
  senext2self(cav);

  if (b->verbose > 1) {
    printf("  Removing point %d on subface (%d, %d, %d).\n",
           pointmark(sapex(abv)), pointmark(sorg(abv)), pointmark(sdest(abv)),
           pointmark(sdest(bcv)));
  }

  spivot(bcv, bccasout);
  sspivot(bcv, bc);
  if (bc.sh != dummysh) {
    if (bcv.sh != bccasout.sh) {
      // 'bcv' is not self-bonded.
      spinsh = bccasout;
      do {
        bccasin = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != bcv.sh);
    } else {
      bccasout.sh = dummysh;
    }
  }
  spivot(cav, cacasout);
  sspivot(cav, ca);
  if (ca.sh != dummysh) {
    if (cav.sh != cacasout.sh) {
      // 'cav' is not self-bonded.
      spinsh = cacasout;
      do {
       cacasin = spinsh;
       spivotself(spinsh);
      } while (spinsh.sh != cav.sh);
    } else {
      cacasout.sh = dummysh;
    }
  }

  // Expand abv to abc.
  setsapex(abv, sdest(bcv));
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      sbond1(bccasin, oldbv);
      sbond1(oldbv, bccasout);
    } else {
      // Bond 'oldbv' to itself.
      sbond(oldbv, oldbv);
    }
    ssbond(oldbv, bc);
  } else {
    sbond(oldbv, bccasout);
  } 
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      sbond1(cacasin, oldva);
      sbond1(oldva, cacasout);
    } else {
      // Bond 'oldva' to itself.
      sbond(oldva, oldva);
    }
    ssbond(oldva, ca);
  } else {
    sbond(oldva, cacasout);
  }

  // Delete two split-out subfaces.
  shellfacedealloc(subfaces, bcv.sh);
  shellfacedealloc(subfaces, cav.sh);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetedge()    Insert a point on an edge of the mesh.                  //
//                                                                           //
// The edge is given by 'splittet'. Assume its four corners are a, b, n1 and //
// n2, where ab is the edge will be split. Around ab may exist any number of //
// tetrahedra. For convenience, they're ordered in a sequence following the  //
// right-hand rule with your thumb points from a to b. Let the vertex set of //
// these tetrahedra be {a, b, n1, n2, ..., n(i)}. NOTE the tetrahedra around //
// ab may not connect to each other (can only happen when ab is a subsegment,//
// hence some faces abn(i) are subfaces).  If ab is a subsegment, abn1 must  //
// be a subface.                                                             //
//                                                                           //
// To split edge ab by a point v is to split all tetrahedra containing ab by //
// v.  More specifically, for each such tetrahedron, an1n2b, it is shrunk to //
// an1n2v, and a new tetrahedra bn2n1v is created. If ab is a subsegment, or //
// some faces of the splitting tetrahedra are subfaces, they must be split   //
// either by calling routine 'splitsubedge()'.                               //
//                                                                           //
// On completion, 'splittet' returns avn1n2.  If 'flipqueue' is not NULL, it //
// returns all faces which may become non-Delaunay after this operation.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splittetedge(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface *bots, *newtops;
  triface oldtop, topcasing;
  triface spintet, tmpbond0, tmpbond1;
  face abseg, splitsh, topsh, spinsh;
  triface worktet;
  face n1n2seg, n2vseg, n1vseg;
  point pa, pb, n1, n2;
  REAL attrib, volume;
  int wrapcount, hitbdry;
  int i, j;

  if (checksubfaces) {
    // Is there a subsegment need to be split together?
    tsspivot(splittet, &abseg);
    if (abseg.sh != dummysh) {
      abseg.shver = 0;
      // Orient the edge direction of 'splittet' be abseg.
      if (org(*splittet) != sorg(abseg)) {
        esymself(*splittet);
      }
    }
  } 
  spintet = *splittet;
  pa = org(spintet);
  pb = dest(spintet);

  if (b->verbose > 1) {
    printf("  Inserting point %d on edge (%d, %d).\n", 
           pointmark(newpoint), pointmark(pa), pointmark(pb));
  }

  // Collect the tetrahedra containing the splitting edge (ab).
  n1 = apex(spintet);
  hitbdry = 0;
  wrapcount = 1;
  if (checksubfaces && abseg.sh != dummysh) {
    // It may happen that some tetrahedra containing ab (a subsegment) are
    //   completely disconnected with others. If it happens, use the face
    //   link of ab to cross the boundary. 
    while (true) {
      if (!fnextself(spintet)) {
        // Meet a boundary, walk through it.
        hitbdry ++;
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pb);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pb);
        // Remember this position (hull face) in 'splittet'.
        *splittet = spintet;
        // Split two hull faces increase the hull size;
        hullsize += 2;
      }
      if (apex(spintet) == n1) break;
      wrapcount ++;
    }
    if (hitbdry > 0) {
      wrapcount -= hitbdry;
    }
  } else {
    // All the tetrahedra containing ab are connected together. If there
    //   are subfaces, 'splitsh' keeps one of them.
    splitsh.sh = dummysh;
    while (hitbdry < 2) {
      if (checksubfaces && splitsh.sh == dummysh) {
        tspivot(spintet, splitsh);
      }
      if (fnextself(spintet)) {
        if (apex(spintet) == n1) break;
        wrapcount++;
      } else {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(*splittet, spintet);
        }
      }
    }
    if (hitbdry > 0) {
      // ab is on the hull.
      wrapcount -= 1;
      // 'spintet' now is a hull face, inverse its edge direction.
      esym(spintet, *splittet);
      // Split two hull faces increases the number of hull faces.
      hullsize += 2;
    }
  }
  
  // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra.
  bots = new triface[wrapcount];
  newtops = new triface[wrapcount];
  // Spin around ab, gather tetrahedra and set up new tetrahedra. 
  spintet = *splittet;
  for (i = 0; i < wrapcount; i++) {
    // Get 'bots[i] = an1n2b'.
    enext2fnext(spintet, bots[i]);
    esymself(bots[i]);
    // Create 'newtops[i]'.
    maketetrahedron(&(newtops[i]));
    // Go to the next.
    fnextself(spintet);
    if (checksubfaces && abseg.sh != dummysh) {
      if (!issymexist(&spintet)) {
        // We meet a hull face, walk through it.
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pb);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pb);
      }
    }
  }
  
  // Set the vertices of updated and new tetrahedra.
  for (i = 0; i < wrapcount; i++) {
    // Update 'bots[i] = an1n2v'.
    setoppo(bots[i], newpoint);
    // Set 'newtops[i] = bn2n1v'.
    n1 = dest(bots[i]);
    n2 = apex(bots[i]);
    // Set 'newtops[i]'.
    setorg(newtops[i], pb);
    setdest(newtops[i], n2);
    setapex(newtops[i], n1);
    setoppo(newtops[i], newpoint);
    // Set the element attributes of a new tetrahedron.
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      attrib = elemattribute(bots[i].tet, j);
      setelemattribute(newtops[i].tet, j, attrib);
    }
    if (b->varvolume) {
      // Set the area constraint of a new tetrahedron.
      volume = volumebound(bots[i].tet);
      setvolumebound(newtops[i].tet, volume);
    }
#ifdef SELF_CHECK
    // Make sure no inversed tetrahedron has been created.
    // volume = orient3d(pa, n1, n2, newpoint);
    // if (volume >= 0.0) {
    //   printf("Internal error in splittetedge(): volume = %.12g.\n", volume);
    // }
    // volume = orient3d(pb, n2, n1, newpoint);
    // if (volume >= 0.0) {
    //   printf("Internal error in splittetedge(): volume = %.12g.\n", volume);
    // }
#endif
  }

  // Bond newtops to topcasings and bots.
  for (i = 0; i < wrapcount; i++) {
    // Get 'oldtop = n1n2va' from 'bots[i]'.
    enextfnext(bots[i], oldtop);
    sym(oldtop, topcasing);
    bond(newtops[i], topcasing);
    if (checksubfaces) {
      tspivot(oldtop, topsh);
      if (topsh.sh != dummysh) {
        tsdissolve(oldtop);
        tsbond(newtops[i], topsh);
      }
    }
    enextfnext(newtops[i], tmpbond0);
    bond(oldtop, tmpbond0);
  }
  // Bond between newtops.
  fnext(newtops[0], tmpbond0);
  enext2fnext(bots[0], spintet); 
  for (i = 1; i < wrapcount; i ++) {
    if (issymexist(&spintet)) {
      enext2fnext(newtops[i], tmpbond1);
      bond(tmpbond0, tmpbond1);
    }
    fnext(newtops[i], tmpbond0);
    enext2fnext(bots[i], spintet); 
  }
  // Bond the last to the first if no boundary.
  if (issymexist(&spintet)) {
    enext2fnext(newtops[0], tmpbond1);
    bond(tmpbond0, tmpbond1);
  }
  if (checksubsegs) {
    for (i = 0; i < wrapcount; i++) {
      enextfnext(bots[i], worktet); // edge n1->n2.
      tsspivot1(worktet, n1n2seg);
      if (n1n2seg.sh != dummysh) {
        enext(newtops[i], tmpbond0);
        tssbond1(tmpbond0, n1n2seg);
      }
      enextself(worktet); // edge n2->v ==> n2->b
      tsspivot1(worktet, n2vseg);
      if (n2vseg.sh != dummysh) {
        tssdissolve1(worktet);
        tssbond1(newtops[i], n2vseg);
      }
      enextself(worktet); // edge v->n1 ==> b->n1
      tsspivot1(worktet, n1vseg);
      if (n1vseg.sh != dummysh) {
        tssdissolve1(worktet);
        enext2(newtops[i], tmpbond0);
        tssbond1(tmpbond0, n1vseg);
      }
    }
  }

  // Is there exist subfaces and subsegment need to be split?
  if (checksubfaces) {
    if (abseg.sh != dummysh) {
      // A subsegment needs be split.
      spivot(abseg, splitsh);
#ifdef SELF_CHECK
      assert(splitsh.sh != dummysh);
#endif
    }
    if (splitsh.sh != dummysh) {
      // Split subfaces (and subsegment).
      findedge(&splitsh, pa, pb);
      splitsubedge(newpoint, &splitsh, (queue *) NULL);
    }
  }

  if (b->verbose > 3) {
    for (i = 0; i < wrapcount; i++) {
      printf("    Updating bots[%i] ", i);
      printtet(&(bots[i]));
      printf("    Creating newtops[%i] ", i);
      printtet(&(newtops[i]));
    }
  }

  if (flipqueue != (queue *) NULL) {
    for (i = 0; i < wrapcount; i++) {
      enqueueflipface(bots[i], flipqueue);
      enqueueflipface(newtops[i], flipqueue);
    }
  }

  // Set the return handle be avn1n2.  It is got by transforming from
  //   'bots[0]' (which is an1n2v).
  fnext(bots[0], spintet); // spintet is an1vn2.
  esymself(spintet); // spintet is n1avn2.
  enextself(spintet); // spintet is avn1n2.
  *splittet = spintet;

  delete [] bots;
  delete [] newtops;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unsplittetedge()    Reverse the operation of splitting an edge, so as to  //
//                     remove the newly inserted point.                      //
//                                                                           //
// Assume the original edge is ab, the tetrahedron containing ab is abn1n2.  //
// After ab was split by a point v, every tetrahedron containing ab (e.g.,   //
// abn1n2) has been split into two (e.g., an1n2v and bn2n1v). 'splittet'     //
// represents avn1n2 (i.e., its destination is v).                           //
//                                                                           //
// To remove point v is to expand each split tetrahedron containing ab (e.g.,//
// (avn1n2 to abn1n2), then delete the redundant one(e.g., vbn1n2). If there //
// exists any subface around ab, routine unsplitsubedge() will be called to  //
// reverse the operation of splitting a edge (or a subsegment) of subfaces.  //
// On completion, point v is not deleted in this routine.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unsplittetedge(triface* splittet)
{
  triface *bots, *newtops;
  triface oldtop, topcasing;
  triface spintet;
  face avseg, splitsh, topsh, spinsh;
  point pa, pv, n1;
  int wrapcount, hitbdry;
  int i;

  spintet = *splittet;
  pa = org(spintet);
  pv = dest(spintet);
  if (checksubfaces) {
    // Is there a subsegment need to be unsplit together?
    tsspivot(splittet, &avseg);
    if (avseg.sh != dummysh) {
      // The subsegment's direction should conform to 'splittet'.
      if (sorg(avseg) != pa) {
        sesymself(avseg);
      }
    }
  } 

  n1 = apex(spintet);
  hitbdry = 0;
  wrapcount = 1;
  if (checksubfaces && avseg.sh != dummysh) {
    // It may happen that some tetrahedra containing ab (a subsegment) are
    //   completely disconnected with others. If it happens, use the face
    //   link of ab to cross the boundary. 
    while (true) {    
      if (!fnextself(spintet)) {
        // Meet a boundary, walk through it.
        hitbdry ++;
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pv);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pv);
        // Remember this position (hull face) in 'splittet'.
        *splittet = spintet;
        // Split two hull faces increase the hull size;
        hullsize += 2;
      }
      if (apex(spintet) == n1) break;
      wrapcount ++;
    }
    if (hitbdry > 0) {
      wrapcount -= hitbdry;
    }
  } else {
    // All the tetrahedra containing ab are connected together. If there
    //   are subfaces, 'splitsh' keeps one of them.
    splitsh.sh = dummysh;
    while (hitbdry < 2) {
      if (checksubfaces && splitsh.sh == dummysh) {
        tspivot(spintet, splitsh);
      }
      if (fnextself(spintet)) {
        if (apex(spintet) == n1) break;
        wrapcount++;
      } else {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(*splittet, spintet);
        }
      }
    }
    if (hitbdry > 0) {
      // ab is on the hull.
      wrapcount -= 1;
      // 'spintet' now is a hull face, inverse its edge direction.
      esym(spintet, *splittet);
      // Split two hull faces increases the number of hull faces.
      hullsize += 2;
    }
  }
  
  // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra.
  bots = new triface[wrapcount];
  newtops = new triface[wrapcount];
  // Spin around av, gather tetrahedra and set up new tetrahedra. 
  spintet = *splittet;
  for (i = 0; i < wrapcount; i++) {
    // Get 'bots[i] = an1n2v'.
    enext2fnext(spintet, bots[i]);
    esymself(bots[i]);
    // Get 'oldtop = n1n2va'.
    enextfnext(bots[i], oldtop);
    // Get 'newtops[i] = 'bn1n2v'
    fnext(oldtop, newtops[i]); // newtop = n1n2bv
    esymself(newtops[i]); // newtop = n2n1bv
    enext2self(newtops[i]); // newtop = bn2n1v
    // Go to the next.
    fnextself(spintet);
    if (checksubfaces && avseg.sh != dummysh) {
      if (!issymexist(&spintet)) {
        // We meet a hull face, walk through it.
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pv);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pv);
      }
    }
  }

  if (b->verbose > 1) {
    printf("  Removing point %d from edge (%d, %d).\n", 
           pointmark(oppo(bots[0])), pointmark(org(bots[0])),
           pointmark(org(newtops[0])));
  }

  for (i = 0; i < wrapcount; i++) {
    // Expand an1n2v to an1n2b.
    setoppo(bots[i], org(newtops[i]));
    // Get 'oldtop = n1n2va' from 'bot[i]'.
    enextfnext(bots[i], oldtop);
    // Get 'topcasing' from 'newtop[i]'
    sym(newtops[i], topcasing);
    // Bond them.
    bond(oldtop, topcasing);
    if (checksubfaces) {
      tspivot(newtops[i], topsh);
      if (topsh.sh != dummysh) {
        tsbond(oldtop, topsh);
      }
    }
    // Delete the tetrahedron above an1n2v.
    tetrahedrondealloc(newtops[i].tet);
  }

  // If there exists any subface, unsplit them.
  if (checksubfaces) {
    if (avseg.sh != dummysh) {
      spivot(avseg, splitsh);
#ifdef SELF_CHECK
      assert(splitsh.sh != dummysh);
#endif
    }
    if (splitsh.sh != dummysh) {
      findedge(&splitsh, pa, pv);
      unsplitsubedge(&splitsh);
    }
  }

  delete [] bots;
  delete [] newtops;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubedge()    Insert a point on an edge of the surface mesh.          //
//                                                                           //
// The splitting edge is given by 'splitsh'. Assume its three corners are a, //
// b, c, where ab is the edge will be split. ab may be a subsegment.         //
//                                                                           //
// To split edge ab is to split all subfaces conatining ab. If ab is not a   //
// subsegment, there are only two subfaces need be split, otherwise, there   //
// may have any number of subfaces need be split. Each splitting subface abc //
// is shrunk to avc, a new subface vbc is created.  It is important to keep  //
// the orientations of edge rings of avc and vbc be the same as abc's. If ab //
// is a subsegment, it is shrunk to av and a new subsegment vb is created.   //
//                                                                           //
// If there are tetrahedra adjoining to the splitting subfaces, they should  //
// be split before calling this routine, so the connection between the new   //
// tetrahedra and the new subfaces can be correctly set.                     //
//                                                                           //
// On completion, 'splitsh' returns avc.  If 'flipqueue' is not NULL, it     //
// returns all edges which may be non-Delaunay.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue)
{
  triface abcd, bace, vbcd, bvce;
  face startabc, spinabc, spinsh;
  face oldbc, bccasin, bccasout;
  face ab, bc;
  face avc, vbc, vbc1;
  face av, vb;
  point pa, pb;

  startabc = *splitsh;
  // Is there a subsegment?
  sspivot(startabc, ab);
  if (ab.sh != dummysh) {
    ab.shver = 0; 
    if (sorg(startabc) != sorg(ab)) {
      sesymself(startabc);
    }
  }
  pa = sorg(startabc);
  pb = sdest(startabc);
  
  if (b->verbose > 1) {
    printf("  Inserting point %d on subedge (%d, %d) %s.\n",
           pointmark(newpoint), pointmark(pa), pointmark(pb),
           (ab.sh != dummysh ? "(seg)" : " "));
  }
  
  // Spin around ab, split every subface containing ab.
  spinabc = startabc;
  do {
    // Adjust spinabc be edge ab.
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Save old configuration at edge bc, if bc has a subsegment, save the
    //   face link of it and dissolve it from bc.
    senext(spinabc, oldbc);
    spivot(oldbc, bccasout);    
    sspivot(oldbc, bc);
    if (bc.sh != dummysh) {
      if (spinabc.sh != bccasout.sh) {
        // 'spinabc' is not self-bonded.
        spinsh = bccasout;
        do {
          bccasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldbc.sh);
      } else {
        bccasout.sh = dummysh;
      }
      ssdissolve(oldbc);
    }
    // Create a new subface.
    makeshellface(subfaces, &vbc);
    // Split abc.
    avc = spinabc;  // Update 'abc' to 'avc'.
    setsdest(avc, newpoint);
    // Make 'vbc' be in the same edge ring as 'avc'. 
    vbc.shver = avc.shver; 
    setsorg(vbc, newpoint); // Set 'vbc'.
    setsdest(vbc, pb);
    setsapex(vbc, sapex(avc));
    if (b->quality && varconstraint) {
      // Copy the area bound into the new subface.
      setareabound(vbc, areabound(avc));
    }
    // Copy the shell marker and shell type into the new subface.
    setshellmark(vbc, shellmark(avc));
    setshelltype(vbc, shelltype(avc));
    if (checkpbcs) {
      // Copy the pbcgroup into the new subface.
      setshellpbcgroup(vbc, shellpbcgroup(avc));
    }
    // Set the connection between updated and new subfaces.
    senext2self(vbc);
    sbond(vbc, oldbc);
    // Set the connection between new subface and casings.
    senext2self(vbc);
    if (bc.sh != dummysh) {
      if (bccasout.sh != dummysh) {
        // Insert 'vbc' into face link.
        sbond1(bccasin, vbc);
        sbond1(vbc, bccasout);
      } else {
        // Bond 'vbc' to itself.
        sbond(vbc, vbc);
      }
      ssbond(vbc, bc);
    } else {
      sbond(vbc, bccasout);
    }
    // Go to next subface at edge ab.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
  } while (spinabc.sh != startabc.sh);

  // Get the new subface vbc above the updated subface avc (= startabc).
  senext(startabc, oldbc);
  spivot(oldbc, vbc);
  if (sorg(vbc) == newpoint) {
    sesymself(vbc);
  }
#ifdef SELF_CHECK
  assert(sorg(vbc) == sdest(oldbc) && sdest(vbc) == sorg(oldbc));
#endif
  senextself(vbc);
  // Set the face link for the new created subfaces around edge vb.
  spinabc = startabc;
  do {
    // Go to the next subface at edge av.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Get the new subface vbc1 above the updated subface avc (= spinabc).
    senext(spinabc, oldbc);
    spivot(oldbc, vbc1);
    if (sorg(vbc1) == newpoint) {
      sesymself(vbc1);
    }
#ifdef SELF_CHECK
    assert(sorg(vbc1) == sdest(oldbc) && sdest(vbc1) == sorg(oldbc));
#endif
    senextself(vbc1);
    // Set the connection: vbc->vbc1.
    sbond1(vbc, vbc1);
    // For the next connection.
    vbc = vbc1;
  } while (spinabc.sh != startabc.sh);

  // Split ab if it is a subsegment.
  if (ab.sh != dummysh) {
    // Update subsegment ab to av.
    av = ab;
    setsdest(av, newpoint);
    // Create a new subsegment vb.
    makeshellface(subsegs, &vb);
    setsorg(vb, newpoint);
    setsdest(vb, pb);
    // vb gets the same mark and segment type as av.
    setshellmark(vb, shellmark(av));
    setshelltype(vb, shelltype(av));
    if (b->quality && varconstraint) {
      // Copy the area bound into the new subsegment.
      setareabound(vb, areabound(av));
    }
    // Save the old connection at ab (re-use the handles oldbc, bccasout).
    senext(av, oldbc);
    spivot(oldbc, bccasout);
    // Bond av and vb (bonded at their "fake" edges).
    senext2(vb, bccasin);
    sbond(bccasin, oldbc);
    if (bccasout.sh != dummysh) {
      // There is a subsegment connecting with ab at b. It will connect
      //   to vb at b after splitting.
      bccasout.shver = 0;
      if (sorg(bccasout) != pb) sesymself(bccasout);
#ifdef SELF_CHECK
      assert(sorg(bccasout) == pb); 
#endif
      senext2self(bccasout);
      senext(vb, bccasin);
      sbond(bccasin, bccasout);
    }
    // Bond all new subfaces (vbc) to vb. 
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      // Bond the new subface and the new subsegment.
      ssbond(vbc, vb);
      // Go to the next.
      spivotself(spinabc);
#ifdef SELF_CHECK
      assert(spinabc.sh != dummysh);
#endif
    } while (spinabc.sh != startabc.sh);
  }

  // Bond the new subfaces to new tetrahedra if they exist.  New tetrahedra
  //   should have been created before calling this routine.
  spinabc = startabc;
  do {
    // Adjust spinabc be edge av.
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Get new subface vbc above the updated subface avc (= spinabc).
    senext(spinabc, oldbc);
    spivot(oldbc, vbc);
    if (sorg(vbc) == newpoint) {
      sesymself(vbc);
    }
    senextself(vbc);
    // Get the adjacent tetrahedra at 'spinabc'.
    stpivot(spinabc, abcd);
    if (abcd.tet != dummytet) {
      findedge(&abcd, sorg(spinabc), sdest(spinabc));
      enextfnext(abcd, vbcd);
      fnextself(vbcd);
#ifdef SELF_CHECK
      assert(vbcd.tet != dummytet);
#endif
      tsbond(vbcd, vbc);
      sym(vbcd, bvce);
      sesymself(vbc);
      tsbond(bvce, vbc);
    } else {
      // One side is empty, check the other side.
      sesymself(spinabc);
      stpivot(spinabc, bace);
      if (bace.tet != dummytet) {
        findedge(&bace, sorg(spinabc), sdest(spinabc));
        enext2fnext(bace, bvce);
        fnextself(bvce);
#ifdef SELF_CHECK
        assert(bvce.tet != dummytet);
#endif
        sesymself(vbc); 
        tsbond(bvce, vbc);
      }
    }
    // Go to the next.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
  } while (spinabc.sh != startabc.sh);
  
  if (b->verbose > 3) {
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      printf("    Updating abc:\n");
      printsh(&spinabc);
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      printf("    Creating vbc:\n");
      printsh(&vbc);
      // Go to the next.
      spivotself(spinabc);
      if (spinabc.sh == dummysh) {
        break; // 'ab' is a hull edge.
      }
    } while (spinabc.sh != startabc.sh);
  }

  if (flipqueue != (queue *) NULL) {
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      senext2(spinabc, oldbc); // Re-use oldbc.
      enqueueflipedge(oldbc, flipqueue);
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      senext(vbc, oldbc); // Re-use oldbc.
      enqueueflipedge(oldbc, flipqueue);
      // Go to the next.
      spivotself(spinabc);
      if (spinabc.sh == dummysh) {
        break; // 'ab' is a hull edge.
      }
    } while (spinabc.sh != startabc.sh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unsplitsubedge()    Reverse the operation of splitting an edge of subface,//
//                     so as to remove a point from the edge.                //
//                                                                           //
// Assume the original edge is ab, the subface containing it is abc. It was  //
// split by a point v into avc, and vbc.  'splitsh' represents avc, further- //
// more, if av is a subsegment, av should be the zero version of the split   //
// subsegment (i.e., av.shver = 0), so we are sure that the destination (v)  //
// of both avc and av is the deleting point.                                 //
//                                                                           //
// To remove point v is to expand avc to abc, delete vbc, do the same for    //
// other subfaces containing av and vb. If av and vb are subsegments, expand //
// av to ab, delete vb.  On completion, point v is not deleted.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unsplitsubedge(face* splitsh)
{
  face startavc, spinavc, spinbcv;
  face oldvc, bccasin, bccasout, spinsh;
  face av, vb, bc;
  point pa, pv, pb;

  startavc = *splitsh;
  sspivot(startavc, av);
  if (av.sh != dummysh) {
    // Orient the direction of subsegment to conform the subface. 
    if (sorg(av) != sorg(startavc)) {
      sesymself(av);
    }
#ifdef SELF_CHECK
    assert(av.shver == 0);
#endif
  }
  senext(startavc, oldvc);
  spivot(oldvc, vb);  // vb is subface vbc
  if (sorg(vb) != sdest(oldvc)) {
    sesymself(vb);
  }
  senextself(vb);
  pa = sorg(startavc);
  pv = sdest(startavc);
  pb = sdest(vb);

  if (b->verbose > 1) {
    printf("  Removing point %d from subedge (%d, %d).\n",
           pointmark(pv), pointmark(pa), pointmark(pb));
  }

  // Spin around av, unsplit every subface containing av.
  spinavc = startavc;
  do {
    // Adjust spinavc be edge av.
    if (sorg(spinavc) != pa) {
      sesymself(spinavc);
    }
    // Save old configuration at edge bc, if bc has a subsegment, save the
    //   face link of it.
    senext(spinavc, oldvc);
    spivot(oldvc, spinbcv);
    if (sorg(spinbcv) != sdest(oldvc)) {
      sesymself(spinbcv);
    }
    senext2self(spinbcv);
    spivot(spinbcv, bccasout);
    sspivot(spinbcv, bc);
    if (bc.sh != dummysh) {
      if (spinbcv.sh != bccasout.sh) {
        // 'spinbcv' is not self-bonded.
        spinsh = bccasout;
        do {
          bccasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != spinbcv.sh);
      } else {
        bccasout.sh = dummysh;
      }
    }
    // Expand avc to abc.
    setsdest(spinavc, pb);
    if (bc.sh != dummysh) {
      if (bccasout.sh != dummysh) {
        sbond1(bccasin, oldvc);
        sbond1(oldvc, bccasout);
      } else {
        // Bond 'oldbc' to itself.
        sbond(oldvc, oldvc);
      }
      ssbond(oldvc, bc);
    } else {
      sbond(oldvc, bccasout);
    }
    // Delete bcv.
    shellfacedealloc(subfaces, spinbcv.sh);
    // Go to next subface at edge av.
    spivotself(spinavc);
    if (spinavc.sh == dummysh) {
      break; // 'av' is a hull edge.
    }
  } while (spinavc.sh != startavc.sh);

  // Is there a subsegment need to be unsplit?
  if (av.sh != dummysh) {
    senext(av, oldvc);  // Re-use oldvc.
    spivot(oldvc, vb);
    vb.shver = 0;
#ifdef SELF_CHECK
    assert(sdest(av) == sorg(vb));
#endif
    senext(vb, spinbcv); // Re-use spinbcv.
    spivot(spinbcv, bccasout);
    // Expand av to ab.
    setsdest(av, pb);
    sbond(oldvc, bccasout);
    // Delete vb.
    shellfacedealloc(subsegs, vb.sh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertsite()    Insert a point into the mesh.                             //
//                                                                           //
// The 'newpoint' is located.  If 'searchtet->tet' is not NULL, the search   //
// for the containing tetrahedron begins from 'searchtet', otherwise, a full //
// point location procedure is called.  If 'newpoint' is found inside a      //
// tetrahedron, the tetrahedron is split into four (by splittetrahedron());  //
// if 'newpoint' lies on a face, the face is split into three, thereby       //
// splitting the two adjacent tetrahedra into six (by splittetface()); if    //
// 'newpoint' lies on an edge, the edge is split into two, thereby, every    //
// tetrahedron containing this edge is split into two. If 'newpoint' lies on //
// an existing vertex, no action is taken, and the value DUPLICATEPOINT  is  //
// returned and 'searchtet' is set to a handle whose origin is the vertex.   //
//                                                                           //
// If 'flipqueue' is not NULL, after 'newpoint' is inserted, it returns all  //
// faces which may become non-Delaunay due to the newly inserted point. Flip //
// operations can be performed as necessary on them to maintain the Delaunay //
// property.                                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::insertsiteresult tetgenmesh::insertsite(point newpoint,
  triface* searchtet, bool approx, queue* flipqueue)
{
  enum locateresult intersect, exactloc;
  point checkpt;
  REAL epspp, checklen;
  int count;

  if (b->verbose > 1) {
    printf("  Insert point to mesh: (%.12g, %.12g, %.12g) %d.\n",
           newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint));
  }

  if (searchtet->tet == (tetrahedron *) NULL) {
    // Search for a tetrahedron containing 'newpoint'.
    searchtet->tet = dummytet;
    exactloc = locate(newpoint, searchtet);
  } else {
    // Start searching from the tetrahedron provided by the caller. 
    exactloc = preciselocate(newpoint, searchtet, tetrahedrons->items);
  }
  intersect = exactloc;
  if (approx && (exactloc != ONVERTEX)) {
    // Adjust the exact location to an approx. location wrt. epsilon.
    epspp = b->epsilon;
    count = 0;
    while (count < 16) {
      intersect = adjustlocate(newpoint, searchtet, exactloc, epspp);
      if (intersect == ONVERTEX) {
        checkpt = org(*searchtet);
        checklen = distance(checkpt, newpoint);
        if (checklen / longest > b->epsilon) {
          epspp *= 1e-2;
          count++;
          continue;
        }
      }
      break;
    }
  }
  // Keep current search state for next searching.
  recenttet = *searchtet; 

  // Insert the point using the right routine
  switch (intersect) {
  case ONVERTEX:
    // There's already a vertex there. Return in 'searchtet' a tetrahedron
    //   whose origin is the existing vertex.
    if (b->verbose > 1) {
      printf("  Not insert for duplicating point.\n");
    }
    return DUPLICATEPOINT;

  case OUTSIDE:
    if (b->verbose > 1) {
      printf("  Not insert for locating outside the mesh.\n");
    }
    return OUTSIDEPOINT;

  case ONEDGE:
    // 'newpoint' falls on an edge.
    splittetedge(newpoint, searchtet, flipqueue);
    return SUCCESSONEDGE;

  case ONFACE:
    // 'newpoint' falls on a face.
    splittetface(newpoint, searchtet, flipqueue);
    return SUCCESSONFACE;

  case INTETRAHEDRON:
    // 'newpoint' falls inside a tetrahedron.
    splittetrahedron(newpoint, searchtet, flipqueue);
    return SUCCESSINTET;
  
  default:
    // Impossible case.
    return OUTSIDEPOINT;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// undosite()    Undo the most recently point insertion.                     //
//                                                                           //
// 'insresult' indicates in where the newpoint has been inserted, i.e., in a //
// tetrahedron, on a face, or on an edge.  A correspoding routine will be    //
// called to undo the point insertion.  'splittet' is a handle represent one //
// of the resulting tetrahedra, but it may be changed after transformation,  //
// even may be dead.  Four points 'torg', ... 'toppo' are the corners which  //
// 'splittet' should have. On finish, 'newpoint' is not removed.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::undosite(enum insertsiteresult insresult, triface* splittet,
  point torg, point tdest, point tapex, point toppo)
{
  // Set the four corners of 'splittet' exactly be 'torg', ... 'toppo'.
  findface(splittet, torg, tdest, tapex);
  if (oppo(*splittet) != toppo) {
    symself(*splittet);
#ifdef SELF_CHECK
    assert(oppo(*splittet) == toppo);
#endif
    // The sym() operation may inverse the edge, correct it if so.
    findedge(splittet, torg, tdest);
  }
  
  // Unsplit the tetrahedron according to 'insresult'.  
  switch (insresult) {
  case SUCCESSINTET:
    // 'splittet' should be the face with 'newpoint' as its opposite.
    unsplittetrahedron(splittet);
    break;
  case SUCCESSONFACE:
    // 'splittet' should be the one of three splitted face with 'newpoint'
    //   as its apex.
    unsplittetface(splittet);
    break;
  case SUCCESSONEDGE:
    // 'splittet' should be the tet with destination is 'newpoint'.
    unsplittetedge(splittet);
    break;
  default: // To omit compile warnings.
    break;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// closeopenface()    Close "open" faces recursively.                        //
//                                                                           //
// This is the support routine of inserthullsite(). A point p which lies out-//
// side of CH(T). p is inserted to T by forming a tet t from p and a visible //
// CH face f. The three sides of f which have p as a vertex is called "open" //
// face. Each open face will be closed by either creating a tet on top of it //
// or become a new CH face.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::closeopenface(triface* openface, queue* flipque)
{
  triface newtet, oldhull;
  triface newopenface, closeface;
  point inspoint, pa, pb, pc;
  REAL attrib, volume;
  int i;

  // Get the new point p.
  inspoint = apex(*openface);
  // Find the old CH face f_o (f and f_o share the same edge). 
  esym(*openface, oldhull);
  while (fnextself(oldhull)) ;
  if (apex(oldhull) != inspoint) {
    // Is f_o visible by p?
    pa = org(oldhull);
    pb = dest(oldhull);
    pc = apex(oldhull);
    if (orient3d(pa, pb, pc, inspoint) < 0.0) {
      // Yes. Create a new tet t above f_o.
      maketetrahedron(&newtet);
      setorg(newtet, pa);
      setdest(newtet, pb);
      setapex(newtet, pc);
      setoppo(newtet, inspoint); 
      for (i = 0; i < in->numberoftetrahedronattributes; i++) {
        attrib = elemattribute(oldhull.tet, i);
        setelemattribute(newtet.tet, i, attrib);
      }
      if (b->varvolume) {
        volume = volumebound(oldhull.tet);
        setvolumebound(newtet.tet, volume);
      }
      // Connect t to T.
      bond(newtet, oldhull);
      // Close f.
      fnext(newtet, newopenface);
      bond(newopenface, *openface);
      // f_o becomes an interior face.
      enqueueflipface(oldhull, flipque);
      // Hull face number decreases.
      hullsize--; 
      // Two faces of t become open face.
      enextself(newtet);
      for (i = 0; i < 2; i++) {
        fnext(newtet, newopenface);
        sym(newopenface, closeface);
        if (closeface.tet == dummytet) {
          closeopenface(&newopenface, flipque);
        }
        enextself(newtet);
      }
    } else {
      // Inivisible. f becomes a new CH face.
      hullsize++;
      // Let 'dummytet' holds f for the next point location.
      dummytet[0] = encode(*openface);
    }
  } else {
    // f_o is co-incident with f --> f is closed by f_o.
    bond(*openface, oldhull);
    // f is an interior face.
    enqueueflipface(*openface, flipque);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// inserthullsite()    Insert a point which lies outside the convex hull.    //
//                                                                           //
// The 'inspoint' p lies outside the tetrahedralization T.  The 'horiz' f is //
// on the convex hull of T, CH(T), which is visible by p (Imagine f is para- //
// llel to the horizon). To insert p into T we have to enlarge the CH(T) and //
// update T so that p is on the new CH(T).                                   //
//                                                                           //
// To enlarge the CH(T).  We need to find the set F of faces which are on CH //
// (T) and visible by p (F can be formed by a depth-first search from f).  p //
// is then inserted into T by mounting new tets formed by p and these faces. //
// Faces of F become interior faces and may non-locally Delaunay.  They are  //
// queued in 'flipqueue' for flip tests.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::inserthullsite(point inspoint, triface* horiz, queue* flipque)
{
  triface firstnewtet;
  triface openface, closeface;
  REAL attrib, volume;
  int i;

  // Let f face to p.
  adjustedgering(*horiz, CW);
  // Create the first tet t (from f and p).
  maketetrahedron(&firstnewtet);
  setorg (firstnewtet, org(*horiz));
  setdest(firstnewtet, dest(*horiz));
  setapex(firstnewtet, apex(*horiz));
  setoppo(firstnewtet, inspoint);
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(horiz->tet, i);
    setelemattribute(firstnewtet.tet, i, attrib);
  }
  if (b->varvolume) {
    volume = volumebound(horiz->tet);
    setvolumebound(firstnewtet.tet, volume);
  }
  // Connect t to T.
  bond(firstnewtet, *horiz);
  // f is not on CH(T) anymore.
  enqueueflipface(*horiz, flipque);
  // Hull face number decreases.
  hullsize--;

  // Call the faces of t which have p as a vertex "open" face.
  for (i = 0; i < 3; i++) {
    // Get an open face f_i of t.
    fnext(firstnewtet, openface);
    // Close f_i if it is still open.
    sym(openface, closeface);
    if (closeface.tet == dummytet) {
      closeopenface(&openface, flipque);
    }
    // Go to the next open face of t.
    enextself(firstnewtet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Terminology: BC(p) and CBC(p), B(p) and C(p).                             //
//                                                                           //
// Given an arbitrary point p,  the Bowyer-Watson cavity BC(p) is formed by  //
// tets whose circumspheres containing p.  The outer faces of BC(p) form a   //
// polyhedron B(p).                                                          //
//                                                                           //
// If p is on a facet F, the constrained Bowyer-Watson cavity CBC(p) on F is //
// formed by subfaces of F whose circumspheres containing p. The outer edges //
// of CBC(p) form a polygon C(p).  B(p) is separated into two parts by C(p), //
// denoted as B_1(p) and B_2(p), one of them may be empty (F is on the hull).//
//                                                                           //
// If p is on a segment S which is shared by n facets.  There exist n C(p)s, //
// each one is a non-closed polygon (without S). B(p) is split into n parts, //
// each of them is denoted as B_i(p), some B_i(p) may be empty.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavitysub()    Form CBC(p) and C(p) on a facet F.                //
//                                                                           //
// Parameters: bp = p, bpseg = S, sublist = CBC(p), subceillist = C(p).      //
//                                                                           //
// CBC(p) contains at least one subface on input; S may be NULL which means  //
// that p is inside a facet. On output, all subfaces of CBC(p) are infected, //
// and the edge rings are oriented to the same halfspace.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavitysub(point bp, face* bpseg, list* sublist,
  list* subceillist)
{
  triface adjtet;
  face startsh, neighsh;
  face checkseg;
  point pa, pb, pc, pd;
  REAL sign;
  int i, j;  

  // Form CBC(p) and C(p) by a broadth-first searching.
  for (i = 0; i < sublist->len(); i++) {
    startsh = * (face *)(* sublist)[i]; // startsh = f.
    // Look for three neighbors of f.
    for (j = 0; j < 3; j++) {
      sspivot(startsh, checkseg);
      if (checkseg.sh == dummysh) {
        // Get its neighbor n.
        spivot(startsh, neighsh);
        // Is n already in CBC(p)?
        if (!sinfected(neighsh)) {
          stpivot(neighsh, adjtet);
          if (adjtet.tet == dummytet) {
            sesymself(neighsh);
            stpivot(neighsh, adjtet);
          }
          // For positive orientation that insphere() test requires.
          adjustedgering(adjtet, CW);
          pa = org(adjtet);
          pb = dest(adjtet);
          pc = apex(adjtet);
          pd = oppo(adjtet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Orient edge ring of n according to that of f.
            if (sorg(neighsh) != sdest(startsh)) sesymself(neighsh);
            // Collect it into CBC(p).
            sinfect(neighsh);
            sublist->append(&neighsh);
          } else {
            subceillist->append(&startsh); // Found an edge of C(p).
          }
        }
      } else {
        // Do not cross a segment.
        if (bpseg != (face *) NULL) {
          if (checkseg.sh != bpseg->sh) {
            subceillist->append(&startsh); // Found an edge of C(p).
          }
        } else {
          subceillist->append(&startsh); // Found an edge of C(p).
        }
      }
      senextself(startsh);
    }
  }

  if (b->verbose > 2) {
    printf("    Collect CBC(%d): %d subfaces, %d edges.\n", pointmark(bp),
           sublist->len(), subceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavityquad()    Form BC_i(p) and B_i(p) in a quadrant.           //
//                                                                           //
// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p).                 //
//                                                                           //
// BC_i(p) contains at least one tet on input. On finish, all tets collected //
// in BC_i(p) are infected. B_i(p) may not closed when p is on segment or in //
// facet. C(p) must be formed before this routine.  Check the infect flag of //
// a subface to identify the unclosed side of B_i(p).  These sides will be   //
// closed by new subfaces of C(p)s.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavityquad(point bp, list* tetlist, list* ceillist)
{
  triface starttet, neightet;
  face checksh;
  point pa, pb, pc, pd;
  REAL sign;
  int i;

  // Form BC_i(p) and B_i(p) by a broadth-first searching.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
      // Try to collect the neighbor of the face (f).
      tspivot(starttet, checksh);
      if (checksh.sh == dummysh) {
        // Get its neighbor n.
        sym(starttet, neightet);
        // Is n already in BC_i(p)?
        if (!infected(neightet)) {
          // For positive orientation that insphere() test requires.
          adjustedgering(neightet, CW);
          pa = org(neightet);
          pb = dest(neightet);
          pc = apex(neightet);
          pd = oppo(neightet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Collect it into BC_i(p).
            infect(neightet);
            tetlist->append(&neightet);
          } else {
            ceillist->append(&starttet); // Found a face of B_i(p).
          }
        }
      } else {
        // Do not cross a boundary face.
        if (!sinfected(checksh)) {
          ceillist->append(&starttet); // Found a face of B_i(p).
        }
      }
    }
  }

  if (b->verbose > 2) {
    printf("    Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp),
           tetlist->len(), ceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavitysegquad()    Form BC_i(p) and B_i(p) in a segment quadrant.//
//                                                                           //
// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p).                 //
//                                                                           //
// BC_i(p) contains at least one tet on input. On finish, all tets collected //
// in BC_i(p) are infected. B_i(p) is not closed. C(p) must be formed before //
// this routine. Check the infect flag of a subface to identify the unclosed //
// sides of B_i(p).  These sides will be closed by new subfaces of C(p)s.    //
//                                                                           //
// During the repair of encroaching subsegments, there may exist locally non-//
// Delaunay faces. These faces are collected in BC_i(p) either.  B_i(p) has  //
// to be formed later than BC_i(p).                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavitysegquad(point bp, list* tetlist,list* ceillist)
{
  triface starttet, neightet, cavtet;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL sign;
  int i;

  // Form BC_i(p) by a broadth-first searching.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
      // Try to collect the neighbor of the face f.
      tspivot(starttet, checksh);
      if (checksh.sh == dummysh) {
        // Get its neighbor n.
        sym(starttet, neightet);
        // Is n already in BC_i(p)?
        if (!infected(neightet)) {
          // For positive orientation that insphere() test requires.
          adjustedgering(neightet, CW);
          pa = org(neightet);
          pb = dest(neightet);
          pc = apex(neightet);
          pd = oppo(neightet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Collect it into BC_i(p).
            infect(neightet);
            tetlist->append(&neightet);
          } else {
            // Check if the face is locally non-Delaunay.
            pe = oppo(starttet);
            sign = insphere(pa, pb, pc, pd, pe);
            if (sign >= 0.0) {
              // Collect it into BC_i(p).
              infect(neightet);
              tetlist->append(&neightet);
            }
          }
        }
      }
    }
  }

  // Generate B_i(p).
  for (i = 0; i < tetlist->len(); i++) {
    cavtet = * (triface *)(* tetlist)[i];
    for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) {
      tspivot(cavtet, checksh);
      if (checksh.sh == dummysh) {
        sym(cavtet, neightet);
        if (!infected(neightet)) {
          ceillist->append(&cavtet); // Found a face of B(p).
        }
      } else {
        // Do not cross a boundary face.
        if (!sinfected(checksh)) {
          ceillist->append(&cavtet); // Found a face of B(p).
        }
      }
    }
  }

  if (b->verbose > 2) {
    printf("    Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp),
           tetlist->len(), ceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavity()    Form BC(p), B(p), CBC(p)s, and C(p)s.                //
//                                                                           //
// If 'bpseg'(S) != NULL, p is on segment S, else, p is on facet containing  //
// 'bpsh' (F).  'n' returns the number of quadrants in BC(p). 'nmax' is the  //
// maximum pre-allocated array length for the lists.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavity(point bp, face* bpseg, face* bpsh, int* n,
  int* nmax, list** sublists, list** subceillists, list** tetlists,
  list** ceillists)
{
  list *sublist;
  triface adjtet;
  face startsh, spinsh;
  point pa, pb;
  int i, j;

  *n = 0;
  if (bpseg != (face *) NULL) {
    // p is on segment S.
    bpseg->shver = 0;
    pa = sorg(*bpseg);
    pb = sdest(*bpseg);
    // Count the number of facets sharing at S.
    spivot(*bpseg, startsh);
    spinsh = startsh;
    do {
      (*n)++; // spinshlist->append(&spinsh);
      spivotself(spinsh);
    } while (spinsh.sh != startsh.sh);
    // *n is the number of quadrants around S.
    if (*n > *nmax) {
      // Reallocate arrays. Should not happen very often.
      delete [] tetlists;
      delete [] ceillists;
      delete [] sublists;
      delete [] subceillists;
      tetlists = new list*[*n];
      ceillists = new list*[*n];
      sublists = new list*[*n];
      subceillists = new list*[*n];
      *nmax = *n;
    }
    // Form CBC(p)s and C(p)s.
    spinsh = startsh;
    for (i = 0; i < *n; i++) {
      sublists[i] = new list(sizeof(face), NULL, 256);
      subceillists[i] = new list(sizeof(face), NULL, 256);
      // Set a subface f to start search.
      startsh = spinsh;
      // Let f face to the quadrant of interest (used in forming BC(p)).
      findedge(&startsh, pa, pb);
      sinfect(startsh);
      sublists[i]->append(&startsh);
      formbowatcavitysub(bp, bpseg, sublists[i], subceillists[i]);
      // Go to the next facet.
      spivotself(spinsh);
    }
  } else if (sublists != (list **) NULL) {
    // p is on a facet.
    *n = 2;
    // Form CBC(p) and C(p).
    sublists[0] = new list(sizeof(face), NULL, 256);
    subceillists[0] = new list(sizeof(face), NULL, 256);
    sinfect(*bpsh);
    sublists[0]->append(bpsh);
    formbowatcavitysub(bp, NULL, sublists[0], subceillists[0]);
  } else {
    // p is inside a tet.
    *n = 1;
  }

  // Form BC_i(p) and B_i(p).
  for (i = 0; i < *n; i++) {
    tetlists[i] = new list(sizeof(triface), NULL, 256);
    ceillists[i] = new list(sizeof(triface), NULL, 256);
    if (sublists != (list **) NULL) {
      // There are C(p)s.
      sublist = ((bpseg == (face *) NULL) ? sublists[0] : sublists[i]);
      // Add all adjacent tets of C_i(p) into BC_i(p).
      for (j = 0; j < sublist->len(); j++) {    
        startsh = * (face *)(* sublist)[j];
        // Adjust the side facing to the right quadrant for C(p).
        if ((bpseg == (face *) NULL) && (i == 1)) sesymself(startsh);
        stpivot(startsh, adjtet);
        if (adjtet.tet != dummytet) {
          if (!infected(adjtet)) {
            infect(adjtet);
            tetlists[i]->append(&adjtet);
          }
        }
      }
      if (bpseg != (face *) NULL) {
        // The quadrant is bounded by another facet.
        sublist = ((i < *n - 1) ? sublists[i + 1] : sublists[0]);
        for (j = 0; j < sublist->len(); j++) {    
          startsh = * (face *)(* sublist)[j];
          // Adjust the side facing to the right quadrant for C(p).
          sesymself(startsh);
          stpivot(startsh, adjtet);
          if (adjtet.tet != dummytet) {
            if (!infected(adjtet)) {
              infect(adjtet);
              tetlists[i]->append(&adjtet);
            }
          }
        }
      }
    }
    // It is possible that BC_i(p) is empty.
    if (tetlists[i]->len() == 0) continue;
    // Collect the rest of tets of BC_i(p) and form B_i(p).
    // if (b->conformdel) {
      // formbowatcavitysegquad(bp, tetlists[i], ceillists[i]);
    // } else {
      formbowatcavityquad(bp, tetlists[i], ceillists[i]);
    // }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// releasebowatcavity()    Undo and free the memory allocated in routine     //
//                         formbowatcavity().                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::releasebowatcavity(face* bpseg, int n, list** sublists,
  list** subceillist, list** tetlists, list** ceillists)
{
  triface oldtet;
  face oldsh;
  int i, j;

  if (sublists != (list **) NULL) {
    // Release CBC(p)s.
    for (i = 0; i < n; i++) {
      // Uninfect subfaces of CBC(p).
      for (j = 0; j < sublists[i]->len(); j++) {
        oldsh = * (face *)(* (sublists[i]))[j];
#ifdef SELF_CHECK
        assert(sinfected(oldsh));
#endif
        suninfect(oldsh);
      }
      delete sublists[i];
      delete subceillist[i];
      sublists[i] = (list *) NULL;
      subceillist[i] = (list *) NULL;
      if (bpseg == (face *) NULL) break;
    }
  }
  // Release BC(p).
  for (i = 0; i < n; i++) {
    // Uninfect tets of BC_i(p).
    for (j = 0; j < tetlists[i]->len(); j++) {
      oldtet = * (triface *)(* (tetlists[i]))[j];
#ifdef SELF_CHECK
      assert(infected(oldtet));
#endif
      uninfect(oldtet);
    }
    delete tetlists[i];
    delete ceillists[i];
    tetlists[i] = (list *) NULL;
    ceillists[i] = (list *) NULL;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// validatebowatcavityquad()    Valid B_i(p).                                //
//                                                                           //
// B_i(p) is valid if all faces of B_i(p) are visible by p, else B_i(p) is   //
// invalid.  Each tet of BC_i(p) which has such a face is marked (uninfect). //
// They will be removed in updatebowatcavityquad().                          //
//                                                                           //
// Return TRUE if B(p) is valid, else, return FALSE.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::validatebowatcavityquad(point bp,list* ceillist,REAL maxcosd)
{
  triface ceiltet;
  point pa, pb, pc;
  REAL ori, cosd;
  int remcount, i;

  // Check the validate of B(p), cut tets having invisible faces.
  remcount = 0;
  for (i = 0; i < ceillist->len(); i++) {
    ceiltet = * (triface *)(* ceillist)[i];
    if (infected(ceiltet)) {
      adjustedgering(ceiltet, CCW);
      pa = org(ceiltet);
      pb = dest(ceiltet);
      pc = apex(ceiltet);
      ori = orient3d(pa, pb, pc, bp);
      if (ori >= 0.0) {
        // Found an invisible face.
        uninfect(ceiltet);
        remcount++;
        continue;
      }
      // If a non-trival 'maxcosd' is given.
      if (maxcosd > -1.0) {
        // Get the maximal dihedral angle of tet abcp.
        tetalldihedral(pa, pb, pc, bp, NULL, &cosd, NULL);
        // Do not form the tet if the maximal dihedral angle is not reduced.
        if (cosd < maxcosd) {
          uninfect(ceiltet);
          remcount++;
        }
      }
    }
  }
  return remcount == 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// updatebowatcavityquad()    Update BC_i(p) and reform B_i(p).              //
//                                                                           //
// B_i(p) is invalid and some tets in BC_i(p) have been marked to be removed //
// in validatebowatcavityquad().  This routine actually remove the cut tets  //
// of BC_i(p) and re-form the B_i(p).                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::updatebowatcavityquad(list* tetlist, list* ceillist)
{
  triface cavtet, neightet;
  face checksh;
  int remcount, i;

  remcount = 0;
  for (i = 0; i < tetlist->len(); i++) {
    cavtet = * (triface *)(* tetlist)[i];
    if (!infected(cavtet)) {
      tetlist->del(i, 1);
      remcount++;
      i--;
    }
  }

  // Are there tets have been cut in BC_i(p)?
  if (remcount > 0) {
    // Re-form B_i(p).
    ceillist->clear();
    for (i = 0; i < tetlist->len(); i++) {
      cavtet = * (triface *)(* tetlist)[i];
      for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) {
        tspivot(cavtet, checksh);
        if (checksh.sh == dummysh) {
          sym(cavtet, neightet);
          if (!infected(neightet)) {
            ceillist->append(&cavtet); // Found a face of B_i(p).
          }
        } else {
          // Do not cross a boundary face.
          if (!sinfected(checksh)) {
            ceillist->append(&cavtet); // Found a face of B_i(p).
          }
        }
      }
    }
    if (b->verbose > 2) {
      printf("    Update BC_i(p): %d tets, %d faces.\n", tetlist->len(),
             ceillist->len());
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// updatebowatcavitysub()    Check and update CBC(p) and C(p).               //
//                                                                           //
// A CBC(p) is valid if all its subfaces are inside or on the hull of BC(p). //
// A subface s of CBC(p) is invalid if it is in one of the two cases:        //
//   (1) s is completely outside BC(p);                                      //
//   (2) s has two adjacent tets but only one of them is in BC(p);           //
// s is removed from CBC(p) if it is invalid. If there is an adjacent tet of //
// s which is in BC(p), it gets removed from BC(p) too. If CBC(p) is updated,//
// C(p) is re-formed.                                                        //
//                                                                           //
// A C(p) is valid if all its edges are on the hull of BC(p).  An edge e of  //
// C(p) may be inside BC(p) if e is a segment and belongs to only one facet. //
// To correct C(p), a tet of BC(p) which shields e gets removed.             //
//                                                                           //
// If BC(p) is formed with locally non-Delaunay check (b->conformdel > 0).   //
// A boundary-consistent check is needed for non-segment edges of C(p). Let  //
// e be such an edge, the subface f contains e and outside C(p) may belong   //
// to B(p) due to the non-coplanarity of the facet definition.  The tet of   //
// BC(p) containing f gets removed to avoid creating a degenerate new tet.   //
//                                                                           //
// 'cutcount' accumulates the total number of cuttets(not only by this call).//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::updatebowatcavitysub(list* sublist, list* subceillist,
  int* cutcount)
{
  triface adjtet, rotface;
  face checksh, neighsh;
  face checkseg;
  point pa, pb, pc;
  REAL ori1, ori2;
  int remcount;
  int i, j;

  remcount = 0;
  // Check the validity of CBC(p).
  for (i = 0; i < sublist->len(); i++) {
    checksh = * (face *)(* sublist)[i];
    // Check two adjacent tets of s.
    for (j = 0; j < 2; j++) {
      stpivot(checksh, adjtet);
      if (adjtet.tet != dummytet) {
        if (!infected(adjtet)) {
          // Could be either case (1) or (2).
          suninfect(checksh); // s survives.
          // If the sym. adjtet exists, it should remove from BC(p) too.
          sesymself(checksh);
          stpivot(checksh, adjtet);
          if (adjtet.tet != dummytet) {
            if (infected(adjtet)) {
              // Found an adj. tet in BC(p), remove it.
              uninfect(adjtet);
              (*cutcount)++;
            }
          }
          // Remove s from C(p).
          sublist->del(i, 1);
          i--;
          remcount++;
          break;
        }
      }
      sesymself(checksh);
    }
  }
  if (remcount > 0) {
    if (b->verbose > 2) {
      printf("    Removed %d subfaces from CBC(p).\n", remcount);
    }
    // Re-generate C(p).
    subceillist->clear();
    for (i = 0; i < sublist->len(); i++) {
      checksh = * (face *)(* sublist)[i];
      for (j = 0; j < 3; j++) {
        spivot(checksh, neighsh);
        if (!sinfected(neighsh)) {
          subceillist->append(&checksh);
        }
        senextself(checksh);
      }
    }
    if (b->verbose > 2) {
      printf("    Update CBC(p): %d subs, %d edges.\n", sublist->len(),
             subceillist->len());
    }
  }

  // Check the validity of C(p).
  for (i = 0; i < subceillist->len(); i++) {
    checksh = * (face *)(* subceillist)[i];
    sspivot(checksh, checkseg);
    if (checkseg.sh != dummysh) {
      // A segment. Check if it is inside BC(p).
      stpivot(checksh, adjtet);
      if (adjtet.tet == dummytet) {
        sesym(checksh, neighsh);
        stpivot(neighsh, adjtet);
      }
      findedge(&adjtet, sorg(checkseg), sdest(checkseg));
      adjustedgering(adjtet, CCW);
      fnext(adjtet, rotface); // It's the same tet.
      // Rotate rotface (f), stop on either of the following cases:
      //   (a) meet a subface, or
      //   (b) enter an uninfected tet, or
      //   (c) rewind back to adjtet.
      do {
        if (!infected(rotface)) break; // case (b)
        tspivot(rotface, neighsh);
        if (neighsh.sh != dummysh) break; // case (a)
        // Go to the next tet of the facing ring.
        fnextself(rotface);
      } while (apex(rotface) != apex(adjtet));
      // Is it case (c)?
      if (apex(rotface) == apex(adjtet)) {
        // The segment is enclosed by BC(p), invalid cavity.
        pa = org(adjtet);
        pb = dest(adjtet);
        pc = apex(adjtet);
        // Find the shield tet and cut it. Notice that the shield tet may
        //   not be unique when there are four coplanar points, ie.,
        //   ori1 * ori2 == 0.0. In such case, choose either of them.
        fnext(adjtet, rotface);
        do {
          fnextself(rotface);
          assert(infected(rotface));
          ori1 = orient3d(pa, pb, pc, apex(rotface));
          ori2 = orient3d(pa, pb, pc, oppo(rotface));
        } while (ori1 * ori2 > 0.0);
        // Cut this tet from BC(p).
        uninfect(rotface);
        (*cutcount)++;
      }
    } else {
      /*// An edge. Check if boundary-consistency should be enforced.
      if (b->conformdel > 0) {
        // Get the adj-sub n at e, it must be outside C(p).
        spivot(checksh, neighsh);
        assert(!sinfected(neighsh));
        // Check if n is on B(p).
        for (j = 0; j < 2; j++) {
          stpivot(neighsh, adjtet);
          if (adjtet.tet != dummytet) {
            if (infected(adjtet)) {
              uninfect(adjtet);
              (*cutcount)++;
            }
          }
          sesymself(neighsh);
        }
      } */
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// trimbowatcavity()    Validate B(p), CBC(p)s and C(p)s, update BC(p).      //
//                                                                           //
// A B(p) is valid if all its faces are visible by p. If a face f of B(p) is //
// found invisible by p, the tet of BC(p) containing f gets removed and B(p) //
// is refromed. The new B(p) may still contain invisible faces by p. Iterat- //
// ively do the above procedure until B(p) is satisfied.                     //
//                                                                           //
// A CBC(p) is valid if each subface of CBC(p) is either on the hull of BC(p)//
// or completely inside BC(p). If a subface s of CBC(p) is not valid, it is  //
// removed from CBC(p) and C(p) is reformed. If there exists a tet t of BC(p)//
// containg s, t is removed from BC(p). The process for validating BC(p) and //
// B(p) is re-excuted.                                                       //
//                                                                           //
// A C(p) is valid if each edge of C(p) is on the hull of BC(p). If an edge  //
// e of C(p) is invalid (e should be a subsegment which only belong to one   //
// facet), a tet of BC(p) which contains e and has two other faces shielding //
// e is removed. The process for validating BC(p) and B(p) is re-excuted.    //
//                                                                           //
// If either BC(p) or CBC(p) becomes empty. No valid BC(p) is found, return  //
// FALSE. else, return TRUE.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::trimbowatcavity(point bp, face* bpseg, int n, list** sublists,
  list** subceillists, list** tetlists, list** ceillists, REAL maxcosd)
{
  bool valflag;
  int oldnum, cutnum, cutcount;
  int i;

  cutnum = 0; // Count the total number of cut-off tets of BC(p).
  valflag = true;

  do {
    // Validate BC(p), B(p).
    for (i = 0; i < n && valflag; i++) {
      oldnum = tetlists[i]->len();
      // Iteratively validate BC_i(p) and B_i(p).
      while (!validatebowatcavityquad(bp, ceillists[i], maxcosd)) {
        // Update BC_i(p) and B_i(p).
        updatebowatcavityquad(tetlists[i], ceillists[i]);
        valflag = tetlists[i]->len() > 0;
      }
      cutnum += (oldnum - tetlists[i]->len());
    }
    if (valflag && (sublists != (list **) NULL)) {
      // Validate CBC(p), C(p).
      cutcount = 0;
      for (i = 0; i < n; i++) {
        updatebowatcavitysub(sublists[i], subceillists[i], &cutcount);
        // Only do once if p is on a facet.
        if (bpseg == (face *) NULL) break; 
      }
      // Are there cut tets?
      if (cutcount > 0) {
        // Squeeze all cut tets in BC(p), keep valflag once it gets FLASE.
        for (i = 0; i < n; i++) {
          if (tetlists[i]->len() > 0) {
            updatebowatcavityquad(tetlists[i], ceillists[i]);
            if (valflag) {
              valflag = tetlists[i]->len() > 0;
            }
          }
        }
        cutnum += cutcount;
        // Go back to valid the updated BC(p).
        continue;
      }
    }
    break; // Leave the while-loop.
  } while (true);

  // Check if any CBC(p) becomes non-empty.
  if (valflag && (sublists != (list **) NULL)) {
    for (i = 0; i < n && valflag; i++) {
      valflag = (sublists[i]->len() > 0);
      if (bpseg == (face *) NULL) break; 
    }
  }

  if (valflag && (cutnum > 0)) {
    // Accumulate counters.
    if (bpseg != (face *) NULL) {
      updsegcount++;
    } else if (sublists != (list **) NULL) {
      updsubcount++;
    } else {
      updvolcount++;
    }
  }

  if (!valflag) {
    // Accumulate counters.
    if (bpseg != (face *) NULL) {
      failsegcount++;
    } else if (sublists != (list **) NULL) {
      failsubcount++;
    } else {
      failvolcount++;
    }
  }

  return valflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// bowatinsertsite()    Insert a point using the Bowyer-Watson method.       //
//                                                                           //
// Parameters: 'bp' = p, 'splitseg' = S, 'n' = the number of quadrants,      //
// 'sublists', an array of CBC_i(p)s, 'subceillists', an array of C_i(p)s,   //
// 'tetlists', an array of BC_i(p)s, 'ceillists', an array of B_i(p)s.       //
//                                                                           //
// If p is inside the mesh domain, then S = NULL, n = 1, CBC(p) and C(p) are //
//   NULLs. 'tetlists[0]' = BC(p), 'ceillists[0]' = B(p).                    //
// If p is on a facet F, then S = NULL, n = 2, and 'subceillists[0]' = C(p), //
//  'subceillists[1]' is not needed (set it to NULL). B_1(p) and B_2(p) are  //
//  in 'ceillists[0]' and 'ceillists[1]'.                                    //
// If p is on a segment S, then F(S) is a list of subfaces around S, and n = //
//   len(F(S)), there are n C_i(p)s and B_i(p)s supplied in 'subceillists[i]'//
//   and 'ceillists[i]'.                                                     //
//                                                                           //
// If 'verlist' != NULL, it returns a list of vertices which connect to p.   //
//   This vertices are used for interpolating size of p.                     //
//                                                                           //
// If 'flipque' != NULL, it returns a list of internal faces of new tets in  //
//   BC(p), faces on C(p)s are excluded. These faces may be locally non-     //
//   Delaunay and will be flipped if they are flippable. Such non-Delaunay   //
//   faces may exist when p is inserted to split an encroaching segment.     //
//                                                                           //
// 'chkencseg', 'chkencsub', and 'chkbadtet' are flags that indicate whether //
// or not there should be checks for the creation of encroached subsegments, //
// subfaces, or bad quality tets. If 'chkencseg' = TRUE, the encroached sub- //
// segments are added to the list of subsegments to be split.                //
//                                                                           //
// On return, 'ceillists' returns Star(p).                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists,
  list** subceillists, list** tetlists, list** ceillists, list* verlist,
  queue* flipque, bool chkencseg, bool chkencsub, bool chkbadtet)
{
  list *ceillist, *subceillist; 
  triface oldtet, newtet, newface, rotface, neightet; 
  face oldsh, newsh, newedge, checksh;
  face spinsh, casingin, casingout;
  face *apsegshs, *pbsegshs;
  face apseg, pbseg, checkseg;
  point pa, pb, pc;
  REAL attrib, volume;
  int idx, i, j, k;

  if (b->verbose > 1) {
    printf("    Insert point %d (%.12g, %.12g, %.12g)", pointmark(bp), bp[0],
           bp[1], bp[2]);
  }
  if (splitseg != (face *) NULL) {
    if (b->verbose > 1) {
      printf(" on segment.\n");
    }
    bowatsegcount++;
  } else {
    if (subceillists != (list **) NULL) {
      if (b->verbose > 1) {
        printf(" on facet.\n");
      }
      bowatsubcount++;
    } else {
      if (b->verbose > 1) {
        printf(" in volume.\n");
      }
      bowatvolcount++;
    }
  }

  // Create new tets to fill B(p).
  for (k = 0; k < n; k++) {
    // Create new tets from each B_i(p).
    ceillist = ceillists[k];
    for (i = 0; i < ceillist->len(); i++) {
      oldtet = * (triface *)(* ceillist)[i];
      adjustedgering(oldtet, CCW);
      pa = org(oldtet);
      pb = dest(oldtet);
      pc = apex(oldtet);
      maketetrahedron(&newtet);
      setorg(newtet, pa);
      setdest(newtet, pb);
      setapex(newtet, pc);
      setoppo(newtet, bp);
      for (j = 0; j < in->numberoftetrahedronattributes; j++) {
        attrib = elemattribute(oldtet.tet, j);
        setelemattribute(newtet.tet, j, attrib);
      }
      if (b->varvolume) {
        volume = volumebound(oldtet.tet);
        if (volume > 0.0) {
          if (!b->fixedvolume && b->refine) {
            // '-r -a' switches and a .vol file case. Enlarge the maximum
            //   volume constraint for the new tets. Hence the new points
            //   only spread near the original constrained tet.
            volume *= 1.2;
          }
        }
        setvolumebound(newtet.tet, volume);
      }
      sym(oldtet, neightet);
      tspivot(oldtet, checksh);
      if (neightet.tet != dummytet) {
        bond(newtet, neightet);
      }
      if (checksh.sh != dummysh) {
        tsbond(newtet, checksh);
      }
      if (verlist != (list *) NULL) {
        // Collect vertices connecting to p.
        idx = pointmark(pa);
        if (idx >= 0) {
          setpointmark(pa, -idx - 1);
          verlist->append(&pa);
        }
        idx = pointmark(pb);
        if (idx >= 0) {
          setpointmark(pb, -idx - 1);
          verlist->append(&pb);
        }
        idx = pointmark(pc);
        if (idx >= 0) {
          setpointmark(pc, -idx - 1);
          verlist->append(&pc);
        }
      }
      // Replace the tet by the newtet for checking the quality.
      * (triface *)(* ceillist)[i] = newtet;
    }
  }
  if (verlist != (list *) NULL) {
    // Uninfect collected vertices.
    for (i = 0; i < verlist->len(); i++) {
      pa = * (point *)(* verlist)[i];
      idx = pointmark(pa);
      setpointmark(pa, -(idx + 1));
    }
  }

  // Connect new tets of B(p). Not all faces of new tets can be connected,
  //   e.g., if there are empty B_i(p)s.
  for (k = 0; k < n; k++) {
    ceillist = ceillists[k];
    for (i = 0; i < ceillist->len(); i++) {
      newtet = * (triface *)(* ceillist)[i];
      newtet.ver = 0;
      for (j = 0; j < 3; j++) {
        fnext(newtet, newface);
        sym(newface, neightet);
        if (neightet.tet == dummytet) {
          // Find the neighbor face by rotating the faces at edge ab.
          esym(newtet, rotface);
          pa = org(rotface);
          pb = dest(rotface);
          while (fnextself(rotface));
          // Do we meet a boundary face?
          tspivot(rotface, checksh);
          if (checksh.sh != dummysh) {
            // Walk through the boundary and continue to rotate faces.
            do {
              findedge(&checksh, pa, pb);
              sfnextself(checksh);
              assert((sorg(checksh) == pa) && (sdest(checksh) == pb));
              stpivot(checksh, rotface);
              if (infected(rotface)) {
                // Meet an old tet of B_i(p). This side is on the hull and
                //   will be connected to a new subface created in C(p).
                break;
              }
              findedge(&rotface, pa, pb);
              while (fnextself(rotface));
              tspivot(rotface, checksh);
            } while (checksh.sh != dummysh);
          }
          // The rotface has edge ab, but it may not have newpt.
          if (apex(rotface) == apex(newface)) { 
            // Bond the two tets together.
            bond(newface, rotface);
            // Queue (uniquely) this face if 'flipque' is given.
            if (flipque != (queue *) NULL) {
              enqueueflipface(newface, flipque);
            }
          }
        }
        enextself(newtet);
      }
    }
  }

  if (subceillists != (list **) NULL) {
    // There are C(p)s.
    if (splitseg != (face *) NULL) {
      // S (ab) is split by p.
      splitseg->shver = 0;
      pa = sorg(*splitseg);
      pb = sdest(*splitseg);
      // Allcate two arrays for saving the subface rings of the two new
      //   segments a->p and p->b.
      apsegshs = new face[n];
      pbsegshs = new face[n];
    }

    // For each C_k(p), do the following:
    //   (1) Create new subfaces to fill C_k(p), insert them into B(p);
    //   (2) Connect new subfaces to each other;
    for (k = 0; k < n; k++) {      
      subceillist = subceillists[k];

      // Check if 'hullsize' should be updated.
      oldsh = * (face *)(* subceillist)[0];
      stpivot(oldsh, neightet);
      if (neightet.tet != dummytet) {
        sesymself(oldsh);
        stpivot(oldsh, neightet);
      }
      if (neightet.tet == dummytet) {
        // The hull size changes.
        hullsize += (subceillist->len() - sublists[k]->len());
      }

      // (1) Create new subfaces to fill C_k(p), insert them into B(p).
      for (i = 0; i < subceillist->len(); i++) {
        oldsh = * (face *)(* subceillist)[i];
        makeshellface(subfaces, &newsh);
        setsorg(newsh, sorg(oldsh));
        setsdest(newsh, sdest(oldsh));
        setsapex(newsh, bp);
        if (b->quality && varconstraint) {
          setareabound(newsh, areabound(oldsh));
        }
        setshellmark(newsh, shellmark(oldsh));
        setshelltype(newsh, shelltype(oldsh));
        if (checkpbcs) {
          setshellpbcgroup(newsh, shellpbcgroup(oldsh));
        }
        // Replace oldsh by newsh at the edge.
        spivot(oldsh, casingout);
        sspivot(oldsh, checkseg);
        if (checkseg.sh != dummysh) {
          // A segment. Insert s into the face ring, ie, s_in -> s -> s_out.
          if (oldsh.sh != casingout.sh) {
            // s is not bonded to itself.
            spinsh = casingout;
            do {
              casingin = spinsh;
              spivotself(spinsh);
            } while (sapex(spinsh) != sapex(oldsh));
            assert(casingin.sh != oldsh.sh); 
            // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out).
            sbond1(casingin, newsh);
            sbond1(newsh, casingout);
          } else {
            // Bond newsh -> newsh.
            sbond(newsh, newsh);
          }
          // Bond the segment.
          ssbond(newsh, checkseg);
        } else {
          // Bond s <-> s_out (and dissolve s_out -> s_old).
          sbond(newsh, casingout);
        }

        // Insert newsh into B(p). Use the coonections of oldsh.
        stpivot(oldsh, neightet);
        if (neightet.tet == dummytet) {
          sesymself(oldsh);
          sesymself(newsh); // Keep the same orientation as oldsh.
          stpivot(oldsh, neightet);
        }
        assert(infected(neightet));
        // Set on the rotating edge.
        findedge(&neightet, sorg(oldsh), sdest(oldsh));
        // Choose the rotating direction (to the inside of B(p)).
        adjustedgering(neightet, CCW);
        rotface = neightet;
        // Rotate face. Stop at a non-infected tet t (not in B(p)) or a
        //   hull face f (on B(p)). Get the neighbor n of t or f.  n is
        //   a new tet that has just been created to fill B(p).
        do {
          fnextself(rotface);
          sym(rotface, neightet);
          if (neightet.tet == dummytet) {
            tspivot(rotface, checksh);
            assert(checksh.sh != dummysh);
            stpivot(checksh, newtet);
            break;
          } else if (!infected(neightet)) {
            sym(neightet, newtet);
            break;
          }
        } while (true);
        assert(newtet.tet != rotface.tet);
        // Set the rotating edge of n.
        findedge(&newtet, sorg(oldsh), sdest(oldsh));
        // Choose the rotating direction (to the inside of B(p)).
        adjustedgering(newtet, CCW);
        fnext(newtet, newface);
        assert(apex(newface) == bp);
        // newsh has already been oriented toward n.
        tsbond(newface, newsh);
        sym(newface, neightet); // 'neightet' maybe outside.
        sesymself(newsh);
        tsbond(neightet, newsh); // Bond them anyway.

        // Replace oldsh by newsh in list.
        * (face *)(* subceillist)[i] = newsh;
      }

      // (2) Connect new subfaces to each other.
      for (i = 0; i < subceillist->len(); i++) {
        // Get a face cdp.
        newsh = * (face *)(* subceillist)[i];
        // Get a new tet containing cdp.
        stpivot(newsh, newtet);
        if (newtet.tet == dummytet) {
          sesymself(newsh);
          stpivot(newsh, newtet);
        }
        for (j = 0; j < 2; j++) {
          if (j == 0) {
            senext(newsh, newedge); // edge dp.
          } else {
            senext2(newsh, newedge); // edge pc.
            sesymself(newedge); // edge cp.
          }
          if (splitseg != (face *) NULL) {
            // Don not operate on newedge if it is ap or pb.
            if (sorg(newedge) == pa) {
              apsegshs[k] = newedge;
              continue;
            } else if (sorg(newedge) == pb) {
              pbsegshs[k] = newedge;
              continue;
            }
          }
          // There should no segment inside the cavity. Check it.
          sspivot(newedge, checkseg);
          assert(checkseg.sh == dummysh);
          spivot(newedge, casingout);
          if (casingout.sh == dummysh) {
            rotface = newtet;
            findedge(&rotface, sorg(newedge), sdest(newedge));
            // Rotate newtet until meeting a new subface which contains
            //   newedge. It must exist since newedge is not a seg.
            adjustedgering(rotface, CCW);
            do {
              fnextself(rotface);
              tspivot(rotface, checksh);
              if (checksh.sh != dummysh) break;
            } while (true);
            findedge(&checksh, sorg(newedge), sdest(newedge));
            sbond(newedge, checksh);
          }
        }
      }
      // Only do once if p is on a facet.
      if (splitseg == (face *) NULL) break;
    } // for (k = 0; k < n; k++)

    if (splitseg != (face *) NULL) {
      // Update a->b to be a->p.
      apseg = *splitseg;
      setsdest(apseg, bp);
      // Create a new subsegment p->b.
      makeshellface(subsegs, &pbseg);
      setsorg(pbseg, bp);
      setsdest(pbseg, pb);
      // p->b gets the same mark and segment type as a->p.
      setshellmark(pbseg, shellmark(apseg));
      setshelltype(pbseg, shelltype(apseg));
      if (b->quality && varconstraint) {
        // Copy the area bound into the new subsegment.
        setareabound(pbseg, areabound(apseg));
      }
      senext(apseg, checkseg);
      // Get the old connection at b of a->b.
      spivot(checkseg, casingout);
      // Bond a->p and p->b together.
      senext2(pbseg, casingin);
      sbond(casingin, checkseg);
      if (casingout.sh != dummysh) {
        // There is a subsegment connect at b of p->b.
        casingout.shver = 0;
#ifdef SELF_CHECK
        assert(sorg(casingout) == pb); 
#endif
        senext2self(casingout);
        senext(pbseg, casingin);
        sbond(casingin, casingout);
      }

      // Bond all new subfaces to a->p and p->b.
      for (i = 0; i < n; i++) {
        spinsh = apsegshs[i];
        findedge(&spinsh, pa, bp);
        ssbond(spinsh, apseg);
        spinsh = pbsegshs[i];
        findedge(&spinsh, bp, pb);
        ssbond(spinsh, pbseg);
      }
      // Bond all subfaces share at a->p together.
      for (i = 0; i < n; i++) {
        spinsh = apsegshs[i];
        if (i < (n - 1)) {
          casingout = apsegshs[i + 1];
        } else {
          casingout = apsegshs[0];
        }
        sbond1(spinsh, casingout);
      }
      // Bond all subfaces share at p->b together.
      for (i = 0; i < n; i++) {
        spinsh = pbsegshs[i];
        if (i < (n - 1)) {
          casingout = pbsegshs[i + 1];
        } else {
          casingout = pbsegshs[0];
        }
        sbond1(spinsh, casingout);
      }
      delete [] apsegshs;
      delete [] pbsegshs;

      // Check for newly encroached subsegments if the flag is set.
      if (chkencseg) {
        // Check if a->p and p->b are encroached by other vertices.
        checkseg4encroach(&apseg, NULL, NULL, true);
        checkseg4encroach(&pbseg, NULL, NULL, true);
        // Check if the adjacent segments are encroached by p.
        tallencsegs(bp, n, ceillists);
      }
    } // if (splitseg != (face *) NULL) 

    // Delete subfaces of old CBC_i(p)s.
    for (k = 0; k < n; k++) {
      for (i = 0; i < sublists[k]->len(); i++) {
        oldsh = * (face *)(* (sublists[k]))[i];
        shellfacedealloc(subfaces, oldsh.sh);
      }
      // Clear the list so that the subs will not get unmarked later in
      //   routine releasebowatcavity() which only frees the memory.
      sublists[k]->clear();
      // Only do once if p is on a facet.
      if (splitseg == (face *) NULL) break; 
    }

    // Check for newly encroached subfaces if the flag is set.
    if (chkencsub) {
      // Check if new subfaces of C_i(p) are encroached by other vertices.
      for (k = 0; k < n; k++) {
        subceillist = subceillists[k];
        for (i = 0; i < subceillist->len(); i++) {
          newsh = * (face *)(* subceillist)[i];
          checksub4encroach(&newsh, NULL, true);
        }
        // Only do once if p is on a facet.
        if (splitseg == (face *) NULL) break; 
      }
      // Check if the adjacent subfaces are encroached by p.
      tallencsubs(bp, n, ceillists);
    }
  } // if (subceillists != (list **) NULL)

  // Delete tets of old BC_i(p)s.
  for (k = 0; k < n; k++) {
    for (i = 0; i < tetlists[k]->len(); i++) {
      oldtet = * (triface *)(* (tetlists[k]))[i];
      tetrahedrondealloc(oldtet.tet);
    }
    // Clear the list so that the tets will not get unmarked later in
    //   routine releasebowatcavity() which only frees the memory.
    tetlists[k]->clear();
  }

  // check for bad quality tets if the flags is set.
  if (chkbadtet) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      for (i = 0; i < ceillist->len(); i++) {
        newtet = * (triface *)(* ceillist)[i];
        checktet4badqual(&newtet, true);
      }
    }
  }

  if (flipque != (queue *) NULL) {
    // Newly created internal faces of BC(p) (excluding faces on C(p)s) are
    //   in 'flipque'.  Some of these faces may be locally non-Delaunay due,
    //   to the existence of non-constrained tets. check and fix them.
    repairflipcount += flip(flipque, NULL);
  }
}

//
// End of mesh transformation routines
//

//
// Begin Delaunay tetrahedralization routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formstarpolyhedron()    Get the star ployhedron of a point 'pt'.          //
//                                                                           //
// The polyhedron P is formed by faces of tets having 'pt' as a vertex.  If  //
// 'complete' is TRUE, P is the complete star of 'pt'. Otherwise, P is boun- //
// ded by subfaces, i.e. P is only part of the star of 'pt'.                 //
//                                                                           //
// 'tetlist' T returns the tets, it has one of such tets on input. Moreover, //
// if t is in T, then oppo(t) = p.  Topologically, T is the star of p;  and  //
// the faces of T is the link of p. 'verlist' V returns the vertices of T.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist,
  bool complete)
{
  triface starttet, neightet;
  face checksh;
  point ver[3];
  int idx, i, j;

  // Get a tet t containing p.
  starttet = * (triface *)(* tetlist)[0];
  // Let oppo(t) = p.
  for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
    if (oppo(starttet) == pt) break;
  }
  assert(starttet.loc < 4);
  // Add t into T.
  * (triface *)(* tetlist)[0] = starttet;
  infect(starttet);
  if (verlist != (list *) NULL) {
    // Add three verts of t into V.
    ver[0] = org(starttet);
    ver[1] = dest(starttet);
    ver[2] = apex(starttet);
    for (i = 0; i < 3; i++) {
      // Mark the vert by inversing the index of the vert.
      idx = pointmark(ver[i]);
      setpointmark(ver[i], -idx - 1); // -1 to distinguish the zero.
      verlist->append(&(ver[i]));
    }
  }

  // Find other tets by a broadth-first search.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    starttet.ver = 0;
    for (j = 0; j < 3; j++) {
      fnext(starttet, neightet);
      tspivot(neightet, checksh);
      // Should we cross a subface.
      if ((checksh.sh == dummysh) || complete) {
        // Get the neighbor n.
        symself(neightet);
        if ((neightet.tet != dummytet) && !infected(neightet)) {
          // Let oppo(n) = p.
          for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
            if (oppo(neightet) == pt) break;
          }
          assert(neightet.loc < 4);
          // Add n into T.
          infect(neightet);
          tetlist->append(&neightet);
          if (verlist != (list *) NULL) {
            // Add the apex vertex in n into V.
            ver[0] = org(starttet);
            ver[1] = dest(starttet);
            findedge(&neightet, ver[0], ver[1]);
            ver[2] = apex(neightet);
            idx = pointmark(ver[2]);
            if (idx >= 0) {
              setpointmark(ver[2], -idx - 1);
              verlist->append(&(ver[2]));
            }
          }
        }
      }
      enextself(starttet);
    }
  }

  // Uninfect tets.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    uninfect(starttet);
  }
  if (verlist != (list *) NULL) {
    // Uninfect vertices.
    for (i = 0; i < verlist->len(); i++) {
      ver[0] = * (point *)(* verlist)[i];
      idx = pointmark(ver[0]);
      setpointmark(ver[0], -(idx + 1));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifypoint()    Unify two distinct points if they're very close.          //
//                                                                           //
// This function is used for dealing with inputs from CAD tools.  Two points //
// p and q are unified if: dist(p, q) / longest < eps.  Where dist() is the  //
// Euclidean distance between p and q, longest is the maximum edge size of   //
// the input point set, eps is the tolerrence specified by user, default is  //
// 1e-6, it can be adjusted by '-T' switch.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult
  loc, REAL eps)
{
  triface symtet, spintet;
  point checkpt, tapex;
  REAL tol;
  bool merged;
  int hitbdry;
  int i;

  merged = false;
  tol = longest * eps;
  if ((loc == OUTSIDE) || (loc == INTETRAHEDRON) || (loc == ONFACE)) {
    // Check p is close to the four corners of the tet.
    for (i = 0; i < 4; i++) {
      checkpt = (point) starttet->tet[4 + i];
      if (distance(testpt, checkpt) < tol) {
        merged = true; // Found a merge point p'.
        break;
      }
    }
    if (!merged && (loc == ONFACE)) {
      // Check the opposite point of the neighbor tet if it exists.
      sym(*starttet, symtet);
      if (symtet.tet != dummytet) {
        checkpt = oppo(symtet);
        if (distance(testpt, checkpt) < tol) {
          merged = true; // Found a merge point p'.
        }
      }
    }
  } else if (loc == ONEDGE) {
    // Check two endpoints of the edge.
    checkpt = org(*starttet);
    if (distance(testpt, checkpt) < tol) {
      merged = true; // Found a merge point p'.
    }
    if (!merged) {
      checkpt = dest(*starttet);
      if (distance(testpt, checkpt) < tol) {
        merged = true; // Found a merge point p'.
      }
    }
    if (!merged) {
      // Check apexes of the faces having the edge.
      spintet = *starttet;
      tapex = apex(*starttet);
      hitbdry = 0;
      do {
        checkpt = apex(spintet);
        if (distance(testpt, checkpt) < tol) {
          merged = true; // Found a merge point p'.
          break;
        }
        if (!fnextself(spintet)) {
          hitbdry++;
          if (hitbdry < 2) {
            esym(*starttet, spintet);
            if (!fnextself(spintet)) {
              hitbdry++;
            }
          }
        }
      } while ((apex(spintet) != tapex) && (hitbdry < 2));
    }
  }
  if (merged) {
    if (b->object != tetgenbehavior::STL) {
      if (!b->quiet) {
        printf("Warning:  Point %d is unified to point %d.\n",
               pointmark(testpt), pointmark(checkpt));
      }
      // Count the number of duplicated points.
      dupverts++;
    }
    // Remember it is a duplicated point.
    setpointtype(testpt, DUPLICATEDVERTEX);
    // Set a pointer to the point it duplicates.
    setpoint2ppt(testpt, checkpt);
  }
  return merged;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrflipdelaunay()   Construct a delaunay tetrahedrization from a set of  //
//                      3D points by the incremental flip algorithm.         //
//                                                                           //
// The incremental flip algorithm (by Edelsbrunner and Shah) can be describ- //
// ed as follows:                                                            //
//                                                                           //
//   S be a set of points in 3D, Let 4 <= i <= n and assume that the         //
//   Delaunay tetrahedralization of the first i-1 points in S is already     //
//   constructed; call it D(i-1). Add the i-th point p_i (belong to S) to    //
//   D(i-1), and restore Delaunayhood by flipping; this result in D(i).      //
//   Repeat this procedure until i = n.                                      //
//                                                                           //
// This strategy always leads to the Delaunay triangulation of a point set.  //
// The return value is the number of convex hull faces of D.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, 
  long arraysize, bool jump, bool merge, REAL eps, queue* flipque)
{
  triface newtet, searchtet;
  point swappt, lastpt;
  enum locateresult loc;
  REAL det, n[3];
  REAL attrib, volume;
  int i, j;
  clock_t loc_start, loc_end;

  if (b->verbose > 0) {
    printf("  Creating initial tetrahedralization.\n");
  }

  // The initial tetrahedralization T only has one tet formed by 4 affinely
  //   linear independent vertices of the point set V = 'insertarray'. The
  //   first point a = insertarray[0].
 
  // Get the second point b, that is not identical or very close to a.
  for (i = 1; i < arraysize; i++) {
    det = distance(insertarray[0], insertarray[i]);
    if (det > (longest * eps)) break;
  }
  if (i == arraysize) {
    printf("\nAll points seem to be identical.\n");
    return;
  } else {
    // Swap to move b from index i to index 1.
    swappt = insertarray[i];
    insertarray[i] = insertarray[1];
    insertarray[1] = swappt;  
  }
  // Get the third point c, that is not collinear with a and b.
  for (i++; i < arraysize; i++) {
    if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) 
      break;
  }
  if (i == arraysize) {
    printf("\nAll points seem to be collinear.\n");
    return;
  } else {
    // Swap to move c from index i to index 2.
    swappt = insertarray[i];
    insertarray[i] = insertarray[2];
    insertarray[2] = swappt;
  }
  // Get the fourth point d, that is not coplanar with a, b, and c.
  for (i++; i < arraysize; i++) {
    det = orient3d(insertarray[0], insertarray[1], insertarray[2],
                   insertarray[i]);
    if (det == 0.0) continue;
    if (!iscoplanar(insertarray[0], insertarray[1], insertarray[2],
                    insertarray[i], det, eps)) break;
  }
  if (i == arraysize) {
    // It's a 2D problem.
    in->mesh_dim = 2;
    // All points are coplanar.
    if (b->plc) {
      // Create an abovepoint. Maybe a surface triangulation can be formed.
      facenormal(insertarray[0], insertarray[1], insertarray[2], n, &det);
      if (det != 0.0) for (j = 0; j < 3; j++) n[j] /= det;
      // Take the average edge length of the bounding box.
      det = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0;
      // Temporarily create a point. It will be removed by jettison();
      makepoint(&lastpt);
      for (j = 0; j < 3; j++) lastpt[j] = insertarray[0][j] + det * n[j];
      abovepoint = lastpt;
      det = orient3d(insertarray[0], insertarray[1], insertarray[2], lastpt);
      // The index of the next inserting point is 3.
      i = 3;
    } else {
      printf("\nAll points seem to be coplanar.\n");
      return;
    }
  } else {
    // Swap to move d from index i to index 3.
    swappt = insertarray[i];
    insertarray[i] = insertarray[3];
    insertarray[3] = swappt;
    lastpt = insertarray[3];
    // The index of the next inserting point is 4.
    i = 4;
  }
  
  // Create the initial tet.
  maketetrahedron(&newtet);
  if (det > 0.0) {
    // For keeping the positive orientation.
    swappt = insertarray[0];
    insertarray[0] = insertarray[1];
    insertarray[1] = swappt;
  }
  if (b->verbose > 2) {
    printf("  Create the first tet (%d, %d, %d, %d).\n",
           pointmark(insertarray[0]), pointmark(insertarray[1]),
           pointmark(insertarray[2]), pointmark(lastpt));
  }
  setorg(newtet, insertarray[0]);
  setdest(newtet, insertarray[1]);
  setapex(newtet, insertarray[2]);
  setoppo(newtet, lastpt);
  if (oldtet != (triface *) NULL) {
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      attrib = elemattribute(oldtet->tet, j);
      setelemattribute(newtet.tet, j, attrib);
    }
    if (b->varvolume) {
      volume = volumebound(oldtet->tet);
      setvolumebound(newtet.tet, volume);
    }
  }
  // Set vertex type be FREEVOLVERTEX if it has no type yet.
  if (pointtype(insertarray[0]) == UNUSEDVERTEX) {
    setpointtype(insertarray[0], FREEVOLVERTEX);
  }
  if (pointtype(insertarray[1]) == UNUSEDVERTEX) {
    setpointtype(insertarray[1], FREEVOLVERTEX);
  }
  if (pointtype(insertarray[2]) == UNUSEDVERTEX) {
    setpointtype(insertarray[2], FREEVOLVERTEX);
  }
  if (pointtype(lastpt) == UNUSEDVERTEX) {
    setpointtype(lastpt, FREEVOLVERTEX);
  }
  // Bond to 'dummytet' for point location.
  dummytet[0] = encode(newtet);
  if (b->verbose > 3) {
    printf("    Creating tetra ");
    printtet(&newtet);
  }
  // At init, all faces of this tet are hull faces.
  hullsize = 4;

  if (b->verbose > 0) {
    printf("  Incrementally inserting points.\n");
  }

  flip23s = flip32s = flip22s = flip44s = 0;
  searchtet.tet = (tetrahedron *) NULL;

  // Insert the rest of points, one by one.
  for (; i < arraysize; i++) {
    // Locate p_i in T.
#ifdef SELF_CHECK
    loc_start = clock();
#endif
    if (jump) {
      loc = locate(insertarray[i], &searchtet);
    } else {
      loc = preciselocate(insertarray[i], &searchtet, tetrahedrons->items);
    }
#ifdef SELF_CHECK
    loc_end = clock();
    tloctime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC;
#endif
    // Keep current search state for next searching.
    recenttet = searchtet;
    if (loc == ONVERTEX) {
      if (b->object != tetgenbehavior::STL) {
        if (!b->quiet) {
          printf("Warning:  Point %d is identical with point %d.\n",
                 pointmark(insertarray[i]), pointmark(org(searchtet)));
        }
      }
      // Count the number of duplicated points.
      dupverts++;
      // Remember it is a duplicated point.
      setpointtype(insertarray[i], DUPLICATEDVERTEX);
      if (b->plc || b->refine) {
        // Set a pointer to the point it duplicates.
        setpoint2ppt(insertarray[i], org(searchtet));
      }
      continue; // p_i is not inserted.
    }
    if (merge) {
      // Unify p_i if it is too close to a point of T.
      if (unifypoint(insertarray[i], &searchtet, loc, eps)) {
        continue; // p_i is not inserted.
      }
    }
    // Insert p_i in T.
    if (loc != OUTSIDE) {
      if (b->verbose > 1) {
        printf("  Insert point %d in tetrahedralization.\n",
               pointmark(insertarray[i]));
      }
      if (loc == INTETRAHEDRON) {
        splittetrahedron(insertarray[i], &searchtet, flipque);
      } else if (loc == ONFACE) {
        splittetface(insertarray[i], &searchtet, flipque);
      } else if (loc == ONEDGE) {
        splittetedge(insertarray[i], &searchtet, flipque);
      }
    } else {
      if (b->verbose > 1) {
        printf("  Insert point %d on convex hull.\n",
               pointmark(insertarray[i]));
      }
      inserthullsite(insertarray[i], &searchtet, flipque);
    }
    if (pointtype(insertarray[i]) == UNUSEDVERTEX) {
      // p_i becomes a (volume) vertex of T.
      setpointtype(insertarray[i], FREEVOLVERTEX);
    }
#ifdef SELF_CHECK
    loc_start = clock();
#endif
    if (!b->noflip) {
      // Recover Delaunayness of T by flipping.
      flip(flipque, NULL); 
    } else {
      lawson(NULL, flipque);
      // T remains regular.
      // flipque->clear();
    }
#ifdef SELF_CHECK
    loc_end = clock();
    tfliptime += ((REAL) (loc_end - loc_start)) / CLOCKS_PER_SEC;
#endif
  }

  if (b->verbose > 0) {
    printf("  %ld Flips (T23 %ld, T32 %ld, T22 %ld, T44 %ld)\n",
      flip23s+flip32s+flip22s+flip44s, flip23s, flip32s, flip22s, flip44s);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizevertices()    Form a Delaunay tetrahedralization.                //
//                                                                           //
// Given a point set V (saved in 'points').  The Delaunay tetrahedralization //
// D of V is created by incrementally inserting vertices. Returns the number //
// of triangular faces bounding the convex hull of D.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::delaunizevertices()
{
  queue *flipque;
  point *insertarray;
  long arraysize;
  int i, j;

  if (!b->quiet) {
    if (!b->noflip) {
      printf("Constructing Delaunay tetrahedralization.\n");
    } else {
      printf("Constructing regular tetrahedralization.\n");
    }
  }

  flipque = new queue(sizeof(badface));
  // Prepare the array of points for inserting.
  arraysize = points->items;
  insertarray = new point[arraysize];  
  points->traversalinit();

  // Randomize the point order.
  // randomseed = b->srandseed;
  for (i = 0; i < arraysize; i++) {
    j = (int) randomnation(i + 1); // 0 <= j <= i;
    insertarray[i] = insertarray[j];
    insertarray[j] = pointtraverse();
  }

  // Use lawson flip.
  b->noflip = 1;

  // Form the DT by incremental flip Delaunay algorithm.
  incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc, b->epsilon,
                   flipque);

  b->noflip = 0;

  delete [] insertarray;
  delete flipque;
  return hullsize;
}

//
// End Delaunay tetrahedralization routines
//

//
// Begin of surface triangulation routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formstarpolygon()    Form the star polygon of a point in facet.           //
//                                                                           //
// The polygon P is formed by all coplanar subfaces having 'pt' as a vertex. //
// P is bounded by segments, e.g, if no segments, P is the full star of pt.  //
//                                                                           //
// 'trilist' T returns the subfaces, it has one of such subfaces on input.   //
// In addition, if f is in T, then sapex(f) = p. 'vertlist' V are verts of P.//
// Topologically, T is the star of p; V and the edges of T are the link of p.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist)
{
  face steinsh, lnextsh, rnextsh;
  face checkseg;
  point pa, pb, pc, pd;
  int i;

  // Get a subface f containing p.
  steinsh = * (face *)(* trilist)[0];
  steinsh.shver = 0; // CCW
  // Let sapex(f) be p.
  for (i = 0; i < 3; i++) {
    if (sapex(steinsh) == pt) break;
    senextself(steinsh);
  }
  assert(i < 3);
  // Add the edge f into list.
  * (face *)(* trilist)[0] = steinsh;
  pa = sorg(steinsh);
  pb = sdest(steinsh);
  if (vertlist != (list *) NULL) {
    // Add two verts a, b into V,
    vertlist->append(&pa);
    vertlist->append(&pb);
  }

  // Rotate edge pa to the left (CW) until meet pb or a segment.
  lnextsh = steinsh;
  pc = pa;
  do {
    senext2self(lnextsh);
    assert(sorg(lnextsh) == pt);
    sspivot(lnextsh, checkseg);
    if (checkseg.sh != dummysh) break; // Do not cross a segment.
    // Get neighbor subface n (must exist).
    spivotself(lnextsh);
    if (lnextsh.sh == dummysh) break; // It's a hull edge.
    // Go to the edge ca opposite to p.
    if (sdest(lnextsh) != pt) sesymself(lnextsh);
    assert(sdest(lnextsh) == pt);
    senext2self(lnextsh);
    // Add n (at edge ca) to T.
    trilist->append(&lnextsh);
    // Add edge ca to E.
    pc = sorg(lnextsh);
    if (pc == pb) break; // Rotate back.
    if (vertlist != (list *) NULL) {
      // Add vert c into V.
      vertlist->append(&pc);
    }
  } while (true);

  if (pc != pb) {
    // Rotate edge bp to the right (CCW) until meet a segment.
    rnextsh = steinsh;
    do {
      senextself(rnextsh);
      assert(sdest(rnextsh) == pt);
      sspivot(rnextsh, checkseg);
      if (checkseg.sh != dummysh) break; // Do not cross a segment.
      // Get neighbor subface n (must exist).
      spivotself(rnextsh);
      if (rnextsh.sh == dummysh) break; // It's a hull edge.
      // Go to the edge bd opposite to p.
      if (sorg(rnextsh) != pt) sesymself(rnextsh);
      assert(sorg(rnextsh) == pt);
      senextself(rnextsh);
      // Add n (at edge bd) to T.
      trilist->append(&rnextsh);
      // Add edge bd to E.
      pd = sdest(rnextsh);
      if (pd == pa) break; // Rotate back.
      if (vertlist != (list *) NULL) {
        // Add vert d into V.
        vertlist->append(&pd);
      }
    } while (true);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// About the 'abovepoint'                                                    //
//                                                                           //
// The 'abovepoint' of a facet is a point which is exactly non-coplanar with //
// the plane containing that facet.  With such an point, the 3D predicates:  //
// orient3d(), and insphere() can be used to substitute the corresponding 2D //
// siblings, e.g. orient2d(), and incircle().  Its location is not critical, //
// but floating-point accuracy is improved if it is nicely placed over the   //
// facet, not too close or too far away.                                     //
//                                                                           //
// We take the convention that the abovepoint of a facet always lies above   //
// the facet. By this convention, given three points a, b, and c in a facet, //
// we say c has the counterclockwise order with ab is corresponding to say   //
// that c is below the plane abp, where p is the lift point.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getfacetabovepoint()    Get a point above a plane pass through a facet.   //
//                                                                           //
// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'//
// is set on return.                                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getfacetabovepoint(face* facetsh)
{
  list *verlist, *trilist, *tetlist;
  triface adjtet;
  face symsh;
  point p1, p2, p3, pa;
  enum locateresult loc;
  REAL smallcos, cosa;
  REAL largevol, volume;
  REAL v1[3], v2[3], len;
  int smallidx, largeidx;
  int shmark;
  int i, j;

  abovecount++;
  // Initialize working lists.
  verlist = new list(sizeof(point *), NULL);
  trilist = new list(sizeof(face), NULL);
  tetlist = new list(sizeof(triface), NULL);

  // Get three pivotal points p1, p2, and p3 in the facet as a base triangle
  //   which is non-trivil and has good base angle (close to 90 degree).

  // p1 is chosen as the one which has the smallest index in pa, pb, pc.
  p1 = sorg(*facetsh);
  pa = sdest(*facetsh);
  if (pointmark(pa) < pointmark(p1)) p1 = pa;
  pa = sapex(*facetsh);
  if (pointmark(pa) < pointmark(p1)) p1 = pa;
  // Form the star polygon of p1.
  trilist->append(facetsh);
  formstarpolygon(p1, trilist, verlist);

  // Get the second pivotal point p2.
  p2 = * (point *)(* verlist)[0];
  // Get vector v1 = p1->p2.
  for (i = 0; i < 3; i++) v1[i] = p2[i] - p1[i];
  len = sqrt(dot(v1, v1));
  assert(len > 0.0);  // p2 != p1.
  for (i = 0; i < 3; i++) v1[i] /= len;

  // Get the third pivotal point p3. p3 is chosen as the one in 'verlist'
  //   which forms an angle with v1 closer to 90 degree than others do.
  smallcos = 1.0; // The cosine value of 0 degree.
  smallidx = 1;   // Default value.
  for (i = 1; i < verlist->len(); i++) {
    p3 = * (point *)(* verlist)[i];
    for (j = 0; j < 3; j++) v2[j] = p3[j] - p1[j];
    len = sqrt(dot(v2, v2));
    if (len > 0.0) { // v2 is not too small.
      cosa = fabs(dot(v1, v2)) / len;
      if (cosa < smallcos) {
        smallidx = i;
        smallcos = cosa;
      }
    }
  }
  assert(smallcos < 1.0); // p1->p3 != p1->p2.
  p3 = * (point *)(* verlist)[smallidx];
  verlist->clear();

  if (tetrahedrons->items > 0l) {
    // Get a tet having p1 as a vertex.
    stpivot(*facetsh, adjtet);
    if (adjtet.tet == dummytet) {
      sesym(*facetsh, symsh);
      stpivot(symsh, adjtet);
    }
    if (adjtet.tet == dummytet) {
      decode(point2tet(p1), adjtet);
      if (isdead(&adjtet)) {
        adjtet.tet = dummytet;
      } else {
        if (!findorg(&adjtet, p1)) {
          adjtet.tet = dummytet;
        }
      }
    }
    if (adjtet.tet == dummytet) {
      loc = locate(p1, &adjtet);
      if (loc == ONVERTEX) {
        setpoint2tet(p1, encode(adjtet));
      } else {
        adjtet.tet = dummytet;
      }
    }
    if (adjtet.tet != dummytet) {
      // Get the star polyhedron of p1.
      tetlist->append(&adjtet);
      formstarpolyhedron(p1, tetlist, verlist, false);
    }
  }

  // Get the abovepoint in 'verlist'. It is the one form the largest valid
  //   volumw with the base triangle over other points in 'verlist.
  largevol = 0.0;
  largeidx = 0;
  for (i = 0; i < verlist->len(); i++) {
    pa = * (point *)(* verlist)[i];
    volume = orient3d(p1, p2, p3, pa);
    if (!iscoplanar(p1, p2, p3, pa, volume, b->epsilon * 1e+2)) {
      if (fabs(volume) > largevol) {
        largevol = fabs(volume);
        largeidx = i;
      }
    }
  }

  // Do we have the abovepoint?
  if (largevol > 0.0) {
    abovepoint = * (point *)(* verlist)[largeidx];
    if (b->verbose > 1) {
      printf("    Chosen abovepoint %d for facet %d.\n", pointmark(abovepoint),
             shellmark(*facetsh));
    }
  } else {
    // Calculate an abovepoint for this facet.
    facenormal(p1, p2, p3, v1, &len);
    if (len != 0.0) for (i = 0; i < 3; i++) v1[i] /= len;
    // Take the average edge length of the bounding box.
    len = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0;
    // Temporarily create a point. It will be removed by jettison();
    makepoint(&abovepoint);
    setpointtype(abovepoint, UNUSEDVERTEX);
    unuverts++;
    for (i = 0; i < 3; i++) abovepoint[i] = p1[i] + len * v1[i];
    if (b->verbose > 1) {
      printf("    Calculated abovepoint %d for facet %d.\n",
             pointmark(abovepoint), shellmark(*facetsh));
    }
  }
  // Save the abovepoint in 'facetabovepointarray'.
  shmark = shellmark(*facetsh);
  facetabovepointarray[shmark] = abovepoint;
  
  delete trilist;
  delete tetlist;
  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// collectcavsubs()    Collect non-locally Delaunay subfaces wrt a point.    //
//                                                                           //
// 'cavsublist' returns the list of subfaces. On input, it conatins at least //
// one subface.                                                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::collectcavsubs(point newpoint, list* cavsublist)
{
  face startsub, neighsub;
  face checkseg;
  point pa, pb, pc;
  REAL sign, ori;
  int i, j;

  // First infect subfaces in 'cavsublist'.
  for (i = 0; i < cavsublist->len(); i++) {
    startsub = * (face *)(* cavsublist)[i];
    sinfect(startsub);
  }
  // Find the other subfaces by a broadth-first searching.
  for (i = 0; i < cavsublist->len(); i++) {
    startsub = * (face *)(* cavsublist)[i];
    for (j = 0; j < 3; j++) {
      sspivot(startsub, checkseg);
      // Is there a segment?
      if (checkseg.sh == dummysh) {
        // No segment. Get the neighbor.
        spivot(startsub, neighsub);
        if (!sinfected(neighsub)) {
          pa = sorg(neighsub);
          pb = sdest(neighsub);
          pc = sapex(neighsub);
          sign = insphere(pa, pb, pc, abovepoint, newpoint);
          ori = orient3d(pa, pb, pc, abovepoint);
          if (sign != 0.0) {
            // Correct the sign.
            sign = ori > 0.0 ? sign : -sign;
          }
          if (sign > 0.0) {
            // neighsub is encroached by newpoint.
            sinfect(neighsub);
            cavsublist->append(&neighsub);
          }
        }
      }
      senextself(startsub);
    }
  }
  // Having found all subfaces, uninfect them before return.
  for (i = 0; i < cavsublist->len(); i++) {
    startsub = * (face *)(* cavsublist)[i];
    suninfect(startsub);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// collectvisiblesubs()    Collect convex hull edges which are visible from  //
//                         the inserting point. Construct new subfaces from  //
//                         these edges and the point.                        //
//                                                                           //
// Let T be the current Delaunay triangulation (of vertices of a facet F).   //
// 'shmark', the index of F in 'in->facetlist' (starts from 1);  'inspoint'  //
// lies outside of T; 'horiz' is a hull edge of T which is visible by it.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::collectvisiblesubs(int shmark, point inspoint, face* horiz,
  queue* flipqueue)
{
  face newsh, hullsh;
  face rightsh, leftsh, spinedge;
  point horg, hdest;
  bool aboveflag;
  REAL ori, sign;

  // Get the sign of abovepoint (so we can assume it is above the plane).  
  adjustedgering(*horiz, CCW);
  horg = sorg(*horiz);
  hdest = sdest(*horiz);
  ori = orient3d(horg, hdest, sapex(*horiz), abovepoint);
  sign = ori > 0.0 ? -1 : 1;

  // Create a new subface above 'horiz'.
  makeshellface(subfaces, &newsh);
  setsorg(newsh, hdest);
  setsdest(newsh, horg);
  setsapex(newsh, inspoint);
  setshellmark(newsh, shmark);
  if (b->quality && varconstraint) {
    setareabound(newsh, areabound(*horiz));
  }
  if (checkpbcs) {
    setshellpbcgroup(newsh, shellpbcgroup(*horiz));
  }
  // Make the connection.
  sbond(newsh, *horiz);
  // 'horiz' becomes interior edge.
  enqueueflipedge(*horiz, flipqueue);
  
  // Finish the hull edges at the right side of the newsh.
  hullsh = *horiz;
  while (1) {
    senext(newsh, rightsh);
    // Get the right hull edge of 'horiz' by spinning inside edges around
    //   'horg' until reaching the 'dummysh'.
    spinedge = hullsh;
    do {
      hullsh = spinedge;
      senext2self(hullsh);
      spivot(hullsh, spinedge);
      if (spinedge.sh == dummysh) break;
      if (sorg(spinedge) != horg) sesymself(spinedge);
      assert(sorg(spinedge) == horg);
    } while (true);
    horg = sorg(hullsh);
    // Test whether 'inspoint' is visible by 'hullsh'.
    ori = orient3d(horg, sdest(hullsh), abovepoint, inspoint);
    ori *= sign;
    aboveflag = ori < 0.0;
    if (aboveflag) {
      // It's visible.
      makeshellface(subfaces, &newsh);
      setsorg(newsh, sdest(hullsh));
      setsdest(newsh, horg);
      setsapex(newsh, inspoint);
      setshellmark(newsh, shmark);
      if (b->quality && varconstraint) {
        setareabound(newsh, areabound(hullsh));
      }
      if (checkpbcs) {
        setshellpbcgroup(newsh, shellpbcgroup(hullsh));
      }
      // Make the connection.
      sbond(newsh, hullsh);
      senext2(newsh, leftsh);
      sbond(leftsh, rightsh);
      // 'hullsh' becomes interior edge.
      enqueueflipedge(hullsh, flipqueue); 
    } else {
      // 'rightsh' is a new hull edge.
      dummysh[0] = sencode(rightsh);
      break;
    }
  }

  // Finish the hull edges at the left side of the newsh.
  hullsh = *horiz;
  spivot(*horiz, newsh);
  while (1) {
    senext2(newsh, leftsh);
    // Get the left hull edge of 'horiz' by spinning edges around 'hdest'.
    spinedge = hullsh;
    do {
      hullsh = spinedge;
      senextself(hullsh);
      spivot(hullsh, spinedge);
      if (spinedge.sh == dummysh) break;
      if (sdest(spinedge) != hdest) sesymself(spinedge);
      assert(sdest(spinedge) == hdest);
    } while (true);
    // Update 'hdest'.
    hdest = sdest(hullsh);
    // Test whether 'inspoint' is visible from 'hullsh'.
    ori = orient3d(sorg(hullsh), hdest, abovepoint, inspoint);
    ori *= sign;
    aboveflag = ori < 0.0;
    if (aboveflag) {
      // It's a visible hull edge.
      makeshellface(subfaces, &newsh);
      setsorg(newsh, hdest);
      setsdest(newsh, sorg(hullsh));
      setsapex(newsh, inspoint);
      setshellmark(newsh, shmark);
      if (b->quality && varconstraint) {
        setareabound(newsh, areabound(hullsh));
      }
      if (checkpbcs) {
        setshellpbcgroup(newsh, shellpbcgroup(hullsh));
      }
      // Make the connection.
      sbond(newsh, hullsh);
      senext(newsh, rightsh);
      sbond(rightsh, leftsh);
      // 'horiz' becomes interior edge.
      enqueueflipedge(hullsh, flipqueue); 
    } else {
      // 'leftsh' is a new hull edge.
      dummysh[0] = sencode(leftsh);
      break;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrflipdelaunaysub()    Create a DT from a 3D coplanar point set using   //
//                          the incremental flip algorithm.                  //
//                                                                           //
// Let T be the current Delaunay triangulation (of vertices of a facet F).   //
// 'shmark', the index of F in 'in->facetlist' (starts from 1).              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist,
  int holes, REAL* holelist, queue* flipque)
{
  face newsh, startsh;
  point *insertarray;
  point swappt;
  pbcdata *pd;
  enum locateresult loc;
  REAL det, area;
  bool aboveflag;
  int arraysize;
  int epscount;
  int fmarker;
  int idx, i, j, k;  

  // Get the point array (saved in 'ptlist').
  insertarray = (point *) ptlist->base;
  arraysize = ptlist->len();
  if (arraysize < 3) return;

  // Do calculation of 'abovepoint' if number of points > 3.
  aboveflag = (arraysize > 3);

  // The initial triangulation T only has one triangle formed by 3 not
  //   cillinear points of the set V = 'insertarray'. The first point:
  //   a = insertarray[0].

  epscount = 0;
  while (true) {
  for (i = 1; i < arraysize; i++) {
    det = distance(insertarray[0], insertarray[i]);
    if (det > (longest * eps)) break;
  }
  if (i < arraysize) {
    // Swap to move b from index i to index 1.
    swappt = insertarray[i];
    insertarray[i] = insertarray[1];
    insertarray[1] = swappt;  
  }
  // Get the third point c, that is not collinear with a and b.
  for (i++; i < arraysize; i++) {
    if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps))
      break;
  }
  if (i < arraysize) {
    // Swap to move c from index i to index 2.
    swappt = insertarray[i];
    insertarray[i] = insertarray[2];
    insertarray[2] = swappt;
    i = 3; // The next inserting point.
  } else {
    // The set of vertices is not good (or nearly degenerate).  However,
    //   a trivial triangulation can be formed (using 3 vertices). It may
    //   be corrected (or deleted) by mergefacet().
    if ((eps == 0.0) || (epscount > 16)) {
      printf("Error:  Invalid PLC.\n");
      printf("  Facet (%d, %d, %d", pointmark(insertarray[0]),
             pointmark(insertarray[1]), pointmark(insertarray[2]));
      if (ptlist->len() > 3) {
        printf(", ...");
      }
      printf(") (%d) is not a valid polygon.\n", shmark);
      terminatetetgen(1);
    }
    // Decrease the eps, and continue to try.
    eps *= 1e-2;
    epscount++;
    continue;
  }
  break;
  } // while (true);

  // Create the initial triangle.
  makeshellface(subfaces, &newsh);
  setsorg(newsh, insertarray[0]);
  setsdest(newsh, insertarray[1]);
  setsapex(newsh, insertarray[2]);
  // Remeber the facet it belongs to.
  setshellmark(newsh, shmark);
  // Set vertex type be FREESUBVERTEX if it has no type yet.
  if (pointtype(insertarray[0]) == FREEVOLVERTEX) {
    setpointtype(insertarray[0], FREESUBVERTEX);
  }
  if (pointtype(insertarray[1]) == FREEVOLVERTEX) {
    setpointtype(insertarray[1], FREESUBVERTEX);
  }
  if (pointtype(insertarray[2]) == FREEVOLVERTEX) {
    setpointtype(insertarray[2], FREESUBVERTEX);
  }
  // Let 'dummysh' point to it (for point location).
  dummysh[0] = sencode(newsh);

  // Are there area constraints?
  if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) {
    idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker.
    for (k = 0; k < in->numberoffacetconstraints; k++) {
      fmarker = (int) in->facetconstraintlist[k * 2];
      if (fmarker == idx) {
        area = in->facetconstraintlist[k * 2 + 1];
        setareabound(newsh, area);
        break;
      }
    }
  }

  // Are there pbc conditions?
  if (checkpbcs) {
    idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker.
    for (k = 0; k < in->numberofpbcgroups; k++) {
      pd = &subpbcgrouptable[k];
      for (j = 0; j < 2; j++) {
        if (pd->fmark[j] == idx) {
          setshellpbcgroup(newsh, k);
          pd->ss[j] = newsh;
        }
      }
    }
  }

  if (aboveflag) {
    // Compute the 'abovepoint' for orient3d().
    abovepoint = facetabovepointarray[shmark];
    if (abovepoint == (point) NULL) {
      getfacetabovepoint(&newsh);
    }
  }

  if (holes > 0) {
    // Project hole points onto the plane containing the facet.
    REAL prj[3];
    for (k = 0; k < holes; k++) {
      projpt2face(&(holelist[k * 3]), insertarray[0], insertarray[1],
                  insertarray[2], prj);
      for (j = 0; j < 3; j++) holelist[k * 3 + j] = prj[j];
    }
  }

  // Incrementally insert the rest of points into T.
  for (; i < arraysize; i++) {
    // Insert p_i.
    startsh.sh = dummysh;
    loc = locatesub(insertarray[i], &startsh, 0, 0.0);
    if (loc == ONFACE) {
      splitsubface(insertarray[i], &startsh, flipque);
    } else if (loc == ONEDGE) {
      splitsubedge(insertarray[i], &startsh, flipque);
    } else if (loc == OUTSIDE) {
      collectvisiblesubs(shmark, insertarray[i], &startsh, flipque);
    } else if (loc == ONVERTEX) {
      // !should not happen!
    }
    // Set p_i's type FREESUBVERTEX if it has no type yet.
    if (pointtype(insertarray[i]) == FREEVOLVERTEX) {
      setpointtype(insertarray[i], FREESUBVERTEX);
    }
    flipsub(flipque);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirectionsub()    Find the first subface in a facet on the path from  //
//                       one point to another.                               //
//                                                                           //
// Finds the subface in the facet that intersects a line segment drawn from  //
// the origin of `searchsh' to the point `tend', and returns the result in   //
// `searchsh'.  The origin of `searchsh' does not change,  even though the   //
// subface returned may differ from the one passed in.                       //
//                                                                           //
// The return value notes whether the destination or apex of the found face  //
// is collinear with the two points in question.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub(
  face* searchsh, point tend)
{
  face checksh;
  point startpoint, leftpoint, rightpoint;
  REAL leftccw, rightccw;
  REAL ori, sign;
  int leftflag, rightflag;

  startpoint = sorg(*searchsh);
  // Find the sign to simulate that abovepoint is 'above' the facet.
  adjustedgering(*searchsh, CCW);
  // Make sure 'startpoint' is the origin.
  if (sorg(*searchsh) != startpoint) senextself(*searchsh);
  rightpoint = sdest(*searchsh);
  leftpoint = sapex(*searchsh);
  ori = orient3d(startpoint, rightpoint, leftpoint, abovepoint);
  sign = ori > 0.0 ? -1 : 1;

  // Is `tend' to the left?
  ori = orient3d(tend, startpoint, abovepoint, leftpoint);
  leftccw = ori * sign;
  leftflag = leftccw > 0.0;
  // Is `tend' to the right?
  ori = orient3d(startpoint, tend, abovepoint, rightpoint);
  rightccw = ori * sign;
  rightflag = rightccw > 0.0;
  if (leftflag && rightflag) {
    // `searchsh' faces directly away from `tend'.  We could go left or
    //   right.  Ask whether it's a triangle or a boundary on the left.
    senext2(*searchsh, checksh);
    spivotself(checksh);
    if (checksh.sh == dummysh) {
      leftflag = 0;
    } else {
      rightflag = 0;
    }
  }
  while (leftflag) {
    // Turn left until satisfied.
    senext2self(*searchsh);
    spivotself(*searchsh);
    if (searchsh->sh == dummysh) {
      printf("Internal error in finddirectionsub():  Unable to find a\n");
      printf("  subface leading from %d to %d.\n", pointmark(startpoint),
             pointmark(tend));
      internalerror();
    }
    if (sorg(*searchsh) != startpoint) sesymself(*searchsh);
    assert(sorg(*searchsh) == startpoint);
    leftpoint = sapex(*searchsh);
    rightccw = leftccw;
    ori = orient3d(tend, startpoint, abovepoint, leftpoint);
    leftccw = ori * sign;
    leftflag = leftccw > 0.0;
  }
  while (rightflag) {
    // Turn right until satisfied.
    spivotself(*searchsh);
    if (searchsh->sh == dummysh) {
      printf("Internal error in finddirectionsub():  Unable to find a\n");
      printf("  subface leading from %d to %d.\n", pointmark(startpoint),
             pointmark(tend));
      internalerror();
    }
    if (sdest(*searchsh) != startpoint) sesymself(*searchsh);
    assert(sdest(*searchsh) == startpoint);
    senextself(*searchsh);
    rightpoint = sdest(*searchsh);
    leftccw = rightccw;
    ori = orient3d(startpoint, tend, abovepoint, rightpoint);
    rightccw = ori * sign;
    rightflag = rightccw > 0.0;
  }
  if (leftccw == 0.0) {
    return LEFTCOLLINEAR;
  } else if (rightccw == 0.0) {
    return RIGHTCOLLINEAR;
  } else {
    return ACROSSEDGE;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertsubseg()    Create a subsegment and insert it between two subfaces. //
//                                                                           //
// The new subsegment ab is inserted at the edge of subface 'tri'.  If ab is //
// not a hull edge, it is inserted between two subfaces.  If 'tri' is a hull //
// face, the initial face ring of ab will be set only one face which is self-//
// bonded.  The final face ring will be constructed in 'unifysegments()'.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertsubseg(face* tri)
{
  face oppotri;
  face newsubseg;
  point pa, pb;
  REAL len;
  int e1, e2;
  int i;

  // Check if there's already a subsegment here.
  sspivot(*tri, newsubseg);
  if (newsubseg.sh == dummysh) {
    // Make new subsegment and initialize its vertices.
    makeshellface(subsegs, &newsubseg);
    pa = sorg(*tri);
    pb = sdest(*tri);
    setsorg(newsubseg, pa);
    setsdest(newsubseg, pb);
    // Are there length constraints?
    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(newsubseg, len);
          break;
        }
      }
    }
    // Bond new subsegment to the two subfaces it is sandwiched between.
    ssbond(*tri, newsubseg);
    spivot(*tri, oppotri);
    // 'oppotri' might be "out space".
    if (oppotri.sh != dummysh) {
      ssbond(oppotri, newsubseg);
    } /* else {
      // Outside! Bond '*tri' to itself.
      sbond(*tri, *tri);
    } */
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsegmentsub()    Scout the first triangle on the path from one point  //
//                      to another, and check for completion (reaching the   //
//                      second point), a collinear point,or the intersection //
//                      of two segments.                                     //
//                                                                           //
// Returns true if the entire segment is successfully inserted, and false if //
// the job must be finished by constrainededge().                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend)
{
  face newsubseg;
  face crosssub, crosssubseg;
  point leftpoint, rightpoint;
  enum finddirectionresult collinear;

  collinear = finddirectionsub(searchsh, tend);
  rightpoint = sdest(*searchsh);
  leftpoint = sapex(*searchsh);
  if (rightpoint == tend || leftpoint == tend) {
    // The segment is already an edge.
    if (leftpoint == tend) {
      senext2self(*searchsh);
    }
    // Insert a subsegment.
    insertsubseg(searchsh);
    return true;
  } else if (collinear == LEFTCOLLINEAR) {
    // We've collided with a vertex between the segment's endpoints.
    // Make the collinear vertex be the triangle's origin.
    senextself(*searchsh); // lprevself(*searchtri);
    // Insert a subsegment.
    insertsubseg(searchsh);
    // Insert the remainder of the segment.
    return scoutsegmentsub(searchsh, tend);
  } else if (collinear == RIGHTCOLLINEAR) {
    // We've collided with a vertex between the segment's endpoints.
    // Insert a subsegment.
    insertsubseg(searchsh);
    // Make the collinear vertex be the triangle's origin.
    senextself(*searchsh); // lnextself(*searchtri);
    // Insert the remainder of the segment.
    return scoutsegmentsub(searchsh, tend);
  } else {
    senext(*searchsh, crosssub); // lnext(*searchtri, crosstri);
    // Check for a crossing segment.
    sspivot(crosssub, crosssubseg);
#ifdef SELF_CHECK
    assert(crosssubseg.sh == dummysh);
#endif
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipedgerecursive()    Flip an edge.                                      //
//                                                                           //
// This is a support routine for inserting segments into a CDT.              //
//                                                                           //
// Let 'flipedge' be ab, and two triangles abc, abd share at it.  ab may not //
// flipable if the four vertices a, b, c, and d are non-convex. If it is the //
// case, recursively flip ad or bd. Return when ab is flipped.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flipedgerecursive(face* flipedge, queue* flipqueue)
{
  face fixupsh;
  point pa, pb, pc, pd;
  REAL oria, orib;
  bool doflip;

  pa = sorg(*flipedge);
  pb = sdest(*flipedge);
  pc = sapex(*flipedge);
  do {
    spivot(*flipedge, fixupsh);    
    pd = sapex(fixupsh);
    oria = orient3d(pc, pd, abovepoint, pa);
    orib = orient3d(pc, pd, abovepoint, pb);
    doflip = (oria * orib < 0.0);
    if (doflip) {
      // Flip the edge (a, b) away.      
      flip22sub(flipedge, flipqueue);
      // Fix flipedge on edge e (c, d).
      findedge(flipedge, pc, pd);
    } else {
      // ab is unflipable. Get the next edge (bd, or da) to flip.
      if (sorg(fixupsh) != pb) sesymself(fixupsh);
      assert(sdest(fixupsh) == pa);
      if (fabs(oria) > fabs(orib)) {
        // acd has larger area. Choose da.
        senextself(fixupsh);
      } else {
        // bcd has larger area. Choose bd.
        senext2self(fixupsh);
      }
      // Flip the edge.
      flipedgerecursive(&fixupsh, flipqueue);
    }
  } while (!doflip);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainededge()    Force a segment into a CDT.                          //
//                                                                           //
// The segment s is recovered by flipping away the edges it intersects, and  //
// triangulating the polygons that form on each side of it.                  //
//                                                                           //
// Generates a single subsegment connecting `tstart' to `tend'. The triangle //
// `startsh' has `tstart' as its origin.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constrainededge(face* startsh, point tend, queue* flipqueue)
{
  point tstart, tright, tleft;
  REAL rori, lori;
  bool collision;

  tstart = sorg(*startsh);
  do {
    // Loop edges oppo to tstart until find one crosses the segment.
    do {
      tright = sdest(*startsh);
      tleft = sapex(*startsh);
      // Is edge (tright, tleft) corss the segment.
      rori = orient3d(tstart, tright, abovepoint, tend);
      collision = (rori == 0.0);
      if (collision) break; // tright is on the segment.
      lori = orient3d(tstart, tleft, abovepoint, tend);
      collision = (lori == 0.0);
      if (collision) { //  tleft is on the segment.
        senext2self(*startsh);
        break;
      }
      if (rori * lori < 0.0) break; // Find the crossing edge.
      // Both points are at one side of the segment. 
      finddirectionsub(startsh, tend);
    } while (true);
    if (collision) break;
    // Get the neighbor face at edge e (tright, tleft).
    senextself(*startsh);
    // Flip the crossing edge.
    flipedgerecursive(startsh, flipqueue);
    // After flip, sorg(*startsh) == tstart.
    assert(sorg(*startsh) == tstart);
  } while (sdest(*startsh) != tend);

  // Insert a subsegment to make the segment permanent.
  insertsubseg(startsh);
  // If there was a collision with an interceding vertex, install another
  //   segment connecting that vertex with endpoint2.
  if (collision) {
    // Insert the remainder of the segment.
    if (!scoutsegmentsub(startsh, tend)) {
      constrainededge(startsh, tend, flipqueue);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoversegment()    Recover a segment in the surface triangulation.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue)
{
  face searchsh;

  if (b->verbose > 2) {
    printf("    Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend));
  }

  // Find a triangle whose origin is the segment's first endpoint.
  searchsh.sh = dummysh;
  // Search for the segment's first endpoint by point location.
  if (locatesub(tstart, &searchsh, 0, 0.0) != ONVERTEX) {
    // Possibly caused by a degenerate subface. Do a brute-force search.
    list *newshlist;
    int i, j;
    newshlist = new list(sizeof(face), NULL, 256);
    // Get new subfaces, do not remove protected segments.
    retrievenewsubs(newshlist, false);
    // Search for a sub contain tstart.
    for (i = 0; i < newshlist->len(); i++) {
      searchsh = * (face *)(* newshlist)[i];
      for (j = 0; j < 3; j++) {
        if (sorg(searchsh) == tstart) break;
        senextself(searchsh);
      }
      if (j < 3) break;
    }
    delete newshlist;
    if (sorg(searchsh) != tstart) {
      printf("Internal error in recoversegment():  Vertex location failed.\n");
      internalerror();
    }
  }
  // Scout the segment and insert it if it is found.
  if (scoutsegmentsub(&searchsh, tend)) {
    // The segment was easily inserted.
    return;
  }  
  // Insert the segment into the triangulation by flips.
  constrainededge(&searchsh, tend, flipqueue);
  // Some edges may need flipping.
  flipsub(flipqueue);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// infecthullsub()    Virally infect all of the triangles of the convex hull //
//                    that are not protected by subsegments.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::infecthullsub(memorypool* viri)
{
  face hulltri, nexttri, starttri;
  face hullsubseg;
  shellface **deadshellface;

  // Find a triangle handle on the hull.
  hulltri.sh = dummysh;
  hulltri.shver = 0;
  spivotself(hulltri);
  adjustedgering(hulltri, CCW);
  // Remember where we started so we know when to stop.
  starttri = hulltri;
  // Go once counterclockwise around the convex hull.
  do {
    // Ignore triangles that are already infected.
    if (!sinfected(hulltri)) {
      // Is the triangle protected by a subsegment?
      sspivot(hulltri, hullsubseg);
      if (hullsubseg.sh == dummysh) {
        // The triangle is not protected; infect it.
        if (!sinfected(hulltri)) {
          sinfect(hulltri);
          deadshellface = (shellface **) viri->alloc();
          *deadshellface = hulltri.sh;
        }
      } 
    }
    // To find the next hull edge, go clockwise around the next vertex.
    senextself(hulltri); // lnextself(hulltri);
    spivot(hulltri, nexttri); // oprev(hulltri, nexttri);
    if (nexttri.sh == hulltri.sh) {
      nexttri.sh = dummysh;  // 'hulltri' is self-bonded.
    } else {
      adjustedgering(nexttri, CCW);
      senextself(nexttri);
    }
    while (nexttri.sh != dummysh) {
      hulltri = nexttri;
      spivot(hulltri, nexttri); // oprev(hulltri, nexttri);
      if (nexttri.sh == hulltri.sh) {
        nexttri.sh = dummysh;  // 'hulltri' is self-bonded.
      } else {
        adjustedgering(nexttri, CCW);
        senextself(nexttri);
      }
    }
  } while (hulltri != starttri);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// plaguesub()    Spread the virus from all infected triangles to any        //
//                neighbors not protected by subsegments.  Delete all        //
//                infected triangles.                                        //
//                                                                           //
// This is the procedure that actually creates holes and concavities.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::plaguesub(memorypool* viri)
{
  face testtri, neighbor, ghostsh;
  face neighborsubseg;
  shellface **virusloop;
  shellface **deadshellface;
  int i;

  // Loop through all the infected triangles, spreading the virus to
  //   their neighbors, then to their neighbors' neighbors.
  viri->traversalinit();
  virusloop = (shellface **) viri->traverse();
  while (virusloop != (shellface **) NULL) {
    testtri.sh = *virusloop;
    // Check each of the triangle's three neighbors.
    for (i = 0; i < 3; i++) {
      // Find the neighbor.
      spivot(testtri, neighbor);
      // Check for a subsegment between the triangle and its neighbor.
      sspivot(testtri, neighborsubseg);
      // Check if the neighbor is nonexistent or already infected.
      if ((neighbor.sh == dummysh) || sinfected(neighbor)) {
        if (neighborsubseg.sh != dummysh) {
          // There is a subsegment separating the triangle from its
          //   neighbor, but both triangles are dying, so the subsegment
          //   dies too.
          shellfacedealloc(subsegs, neighborsubseg.sh);
          if (neighbor.sh != dummysh) {
            // Make sure the subsegment doesn't get deallocated again
            //   later when the infected neighbor is visited.
            ssdissolve(neighbor);
          }
        }
      } else {                   // The neighbor exists and is not infected.
        if (neighborsubseg.sh == dummysh) {
          // There is no subsegment protecting the neighbor, so the
          //   neighbor becomes infected.
          sinfect(neighbor);
          // Ensure that the neighbor's neighbors will be infected.
          deadshellface = (shellface **) viri->alloc();
          *deadshellface = neighbor.sh;
        } else {               // The neighbor is protected by a subsegment.
          // Remove this triangle from the subsegment.
          ssbond(neighbor, neighborsubseg);
        }
      }
      senextself(testtri);
    }
    virusloop = (shellface **) viri->traverse();
  }

  ghostsh.sh = dummysh; // A handle of outer space.
  viri->traversalinit();
  virusloop = (shellface **) viri->traverse();
  while (virusloop != (shellface **) NULL) {
    testtri.sh = *virusloop;
    // Record changes in the number of boundary edges, and disconnect
    //   dead triangles from their neighbors. 
    for (i = 0; i < 3; i++) {
      spivot(testtri, neighbor);
      if (neighbor.sh != dummysh) {
        // Disconnect the triangle from its neighbor.
        // sdissolve(neighbor);
        sbond(neighbor, ghostsh); 
      }
      senextself(testtri);
    }
    // Return the dead triangle to the pool of triangles.
    shellfacedealloc(subfaces, testtri.sh);
    virusloop = (shellface **) viri->traverse();
  }
  // Empty the virus pool.
  viri->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carveholessub()    Find the holes and infect them.  Find the area         //
//                    constraints and infect them.  Infect the convex hull.  //
//                    Spread the infection and kill triangles.  Spread the   //
//                    area constraints.                                      //
//                                                                           //
// This routine mainly calls other routines to carry out all these functions.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carveholessub(int holes, REAL* holelist, memorypool *viri)
{
  face searchtri, triangleloop;
  shellface **holetri;
  enum locateresult intersect;
  int i;

  // Mark as infected any unprotected triangles on the boundary.
  //   This is one way by which concavities are created.
  infecthullsub(viri);

  if (holes > 0) {
    // Infect each triangle in which a hole lies.
    for (i = 0; i < 3 * holes; i += 3) {
      // Ignore holes that aren't within the bounds of the mesh.
      if ((holelist[i] >= xmin) && (holelist[i] <= xmax)
          && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax)
          && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) {
        // Start searching from some triangle on the outer boundary.
        searchtri.sh = dummysh;
        // Find a triangle that contains the hole.
        intersect = locatesub(&holelist[i], &searchtri, 0, 0.0);
        if ((intersect != OUTSIDE) && (!sinfected(searchtri))) {
          // Infect the triangle.  This is done by marking the triangle
          //   as infected and including the triangle in the virus pool.
          sinfect(searchtri);
          holetri = (shellface **) viri->alloc();
          *holetri = searchtri.sh;
        }
      }
    }
  }

  if (viri->items > 0) {
    // Carve the holes and concavities.
    plaguesub(viri);
  }
  // The virus pool should be empty now.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// triangulate()    Triangulate a PSLG into a CDT.                           //
//                                                                           //
// A Planar Straight Line Graph (PSLG) P is actually a 2D polygonal region,  //
// possibly contains holes, segments and vertices in its interior. P is tri- //
// angulated into a set of _subfaces_ forming a CDT of P.                    //
//                                                                           //
// The vertices and segments of P are found in 'ptlist' and 'conlist', resp- //
// ectively. 'holelist' contains a list of hole points. 'shmark' will be set //
// to all subfaces of P.                                                     //
//                                                                           //
// The CDT is created directly in the pools 'subfaces' and 'subsegs'. It can //
// be retrived by a broadth-first searching starting from 'dummysh[0]'(debug //
// function 'outsurfmesh()' does it).                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist,
  int holes, REAL* holelist, memorypool* viri, queue* flipqueue)
{
  face newsh;
  point *cons;
  int i;

  if (b->verbose > 1) {
    printf("    %d vertices, %d segments", ptlist->len(), conlist->len());
    if (holes > 0) {
      printf(", %d holes", holes);
    }
    printf(", shmark: %d.\n", shmark);
  }

  // Create the DT of V by the 2D incremental flip algorithm.
  incrflipdelaunaysub(shmark, eps, ptlist, holes, holelist, flipqueue);
  // Recover boundary edges.
  if (ptlist->len() > 3) {
    // Insert segments into the DT.
    for (i = 0; i < conlist->len(); i++) {
      cons = (point *)(* conlist)[i];
      recoversegment(cons[0], cons[1], flipqueue);        
    }
    // Carve holes and concavities.
    carveholessub(holes, holelist, viri);
  } else if (ptlist->len() == 3) {
    // Insert 3 segments directly.
    newsh.sh = dummysh;
    newsh.shver = 0;
    spivotself(newsh);
    for (i = 0; i < 3; i++) {
      insertsubseg(&newsh);
      senextself(newsh);
    }
  } else if (ptlist->len() == 2) {
    // This facet is actually a segment. It is not support by the mesh data
    //   strcuture. Hence the segment will not be maintained in the mesh.
    //   However, during segment recovery, the segment can be processed.
    cons = (point *)(* conlist)[0];
    makeshellface(subsegs, &newsh);
    setsorg(newsh, cons[0]);
    setsdest(newsh, cons[1]);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// retrievenewsubs()    Retrieve newly created subfaces.                     //
//                                                                           //
// The new subfaces created by triangulate() can be found by a broadth-first //
// searching starting from 'dummysh[0]'.                                     //
//                                                                           //
// 'newshlist' (empty on input) returns the retrieved subfaces. Each edge on //
// the hull is bound to 'dummysh' and protected by a segment. If 'removeseg' //
// is TRUE, the segment is removed.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::retrievenewsubs(list* newshlist, bool removeseg)
{
  face startsh, neighsh;
  face deadseg;
  int i, j;

  // The first new subface is found at dummysh[0].
  startsh.sh = dummysh;
  startsh.shver = 0;
  spivotself(startsh);
  assert(startsh.sh != dummysh);
  sinfect(startsh);
  newshlist->append(&startsh);

  // Find the rest of new subfaces by a broadth-first searching.
  for (i = 0; i < newshlist->len(); i++) {
    // Get a new subface s.
    startsh = * (face *)(* newshlist)[i];
    for (j = 0; j < 3; j++) {
      spivot(startsh, neighsh);
      if (neighsh.sh != dummysh) {
        if (!sinfected(neighsh)) {
          // Discovered a new subface.
          sinfect(neighsh);
          newshlist->append(&neighsh);
        }
      } else {
        // Found a boundary edge. 
        if (removeseg) {
          // This side of s may be protected by a segment.
          sspivot(startsh, deadseg);
          if (deadseg.sh != dummysh) {
            // Detach it from s.
            ssdissolve(startsh);
            // Delete the segment.
            shellfacedealloc(subsegs, deadseg.sh);
          }
        }
      }
      senextself(startsh);
    }
  }
  for (i = 0; i < newshlist->len(); i++) {
    startsh = * (face *)(* newshlist)[i];
    suninfect(startsh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifysegments()    Unify identical segments and build facet connections.  //
//                                                                           //
// After creating the surface mesh. Each facet has its own segments.  There  //
// are duplicated segments between adjacent facets.  This routine has three  //
// purposes:                                                                 //
//   (1) identify the set of segments which have the same endpoints and      //
//       unify them into one segment, remove redundant ones;                 //
//   (2) create the face rings of the unified segments, hence setup the      //
//       connections between facets; and                                     //
//   (3) set a unique marker (1-based) for each segment.                     //
// On finish, each segment is unique and the face ring around it (right-hand //
// rule) is constructed. The connections between facets-facets are setup.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unifysegments()
{
  list *sfacelist;
  shellface **facesperverlist;
  face subsegloop, testseg;
  face sface, sface1, sface2;
  point torg, tdest;
  REAL da1, da2;
  int *idx2facelist;
  int segmarker;
  int idx, k, m;

  if (b->verbose > 0) {
    printf("  Unifying segments.\n");
  }

  // Compute a mapping from indices of vertices to subfaces.
  makesubfacemap(idx2facelist, facesperverlist);
  // Initialize 'sfacelist' for constructing the face link of each segment.
  sfacelist = new list(sizeof(face), NULL); 
  
  segmarker = 1;
  subsegs->traversalinit();
  subsegloop.sh = shellfacetraverse(subsegs);
  while (subsegloop.sh != (shellface *) NULL) {
    subsegloop.shver = 0; // For sure.
    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 = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) {
      sface.sh = facesperverlist[k];
      sface.shver = 0;
      // sface may be died due to the removing of duplicated subfaces.
      if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) {
        // 'sface' contains this segment.
        findedge(&sface, torg, tdest);
        // Save it in 'sfacelist'.
        if (sfacelist->len() < 2) {
          sfacelist->append(&sface);
        } else {
          for (m = 0; m < sfacelist->len() - 1; m++) {
            sface1 = * (face *)(* sfacelist)[m];
            sface2 = * (face *)(* sfacelist)[m + 1];
            da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface));
            da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2));
            if (da1 < da2) {
              break;  // Insert it after m.
            }
          }
          sfacelist->insert(m + 1, &sface);
        }
      }
    }
    if (b->verbose > 1) {
      printf("    Identifying %d segments of (%d  %d).\n", sfacelist->len(),
             pointmark(torg), pointmark(tdest));
    }
    // Set the connection between this segment and faces containing it,
    //   at the same time, remove redundant segments.
    for (k = 0; k < sfacelist->len(); k++) {
      sface = *(face *)(* sfacelist)[k];
      sspivot(sface, testseg);
      // If 'testseg' is not 'subsegloop', it is a redundant segment that
      //   needs be removed. BE CAREFUL it may already be removed. Do not
      //   remove it twice, i.e., do test 'isdead()' together.
      if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) {
        shellfacedealloc(subsegs, testseg.sh);
      }
      // 'ssbond' bonds the subface and the segment together, and dissloves
      //   the old bond as well.
      ssbond(sface, subsegloop);
    }
    // Set connection between these faces.
    sface = *(face *)(* sfacelist)[0];
    for (k = 1; k <= sfacelist->len(); k++) {
      if (k < sfacelist->len()) {
        sface1 = *(face *)(* sfacelist)[k];
      } else {
        sface1 = *(face *)(* sfacelist)[0];    // Form a face loop.
      }
      /*
      // Check if these two subfaces are the same. It is possible when user
      //   defines one facet (or polygon) two or more times. If they are,
      //   they should not be bonded together, instead of that, one of them
      //   should be delete from the surface mesh.
      if ((sfacelist->len() > 1) && sapex(sface) == sapex(sface1)) {
        // They are duplicated faces.
        if (b->verbose > 0) {
          printf("  A duplicated subface (%d, %d, %d) is removed.\n",
                 pointmark(torg), pointmark(tdest), pointmark(sapex(sface)));
        }
        if (k == sfacelist->len()) {
          // 'sface' is the last face, however, it is same as the first one.
          //   In order to form the ring, we have to let the second last
          //   face bond to the first one 'sface1'.
          shellfacedealloc(subfaces, sface.sh);
          assert(sfacelist->len() >= 2);
          assert(k == sfacelist->len());
          sface = *(face *)(* sfacelist)[k - 2];
        } else {
          // 'sface1' is in the middle and may be the last one. 
          shellfacedealloc(subfaces, sface1.sh);
          // Skip this face and go to the next one.
          continue;
        }
      }
      */ 
      if (b->verbose > 2) {
        printf("    Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n",
               pointmark(torg), pointmark(tdest), pointmark(sapex(sface)),
               pointmark(torg), pointmark(tdest), pointmark(sapex(sface1)));
      }
      sbond1(sface, sface1);
      sface = sface1;
    }
    // Set the unique segment marker into the unified segment.
    setshellmark(subsegloop, segmarker);
    // Increase the marker.
    segmarker++;
    // Clear the working list.
    sfacelist->clear(); 
    subsegloop.sh = shellfacetraverse(subsegs);
  }

  delete [] idx2facelist;
  delete [] facesperverlist;
  delete sfacelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// mergefacets()    Merge adjacent facets to be one facet if they are        //
//                  coplanar and have the same boundary marker.              //
//                                                                           //
// Segments between two merged facets will be removed from the mesh.  If all //
// segments around a vertex have been removed, change its vertex type to be  //
// FREESUBVERTEX. Edge flips will be performed to ensure the Delaunayness of //
// the triangulation of merged facets.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::mergefacets(queue* flipqueue)
{
  face parentsh, neighsh, neineighsh;
  face segloop;
  point eorg, edest;
  REAL ori;
  bool mergeflag, pbcflag;
  int* segspernodelist;
  int fidx1, fidx2;
  int i, j;

  if (b->verbose > 0) {
    printf("  Merging coplanar facets.\n");
  }
  // Create and initialize 'segspernodelist'.
  segspernodelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) segspernodelist[i] = 0;

  // Loop the segments, counter the number of segments sharing each vertex.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 2; i++) {
      j = pointmark((point) segloop.sh[3 + i]);
      segspernodelist[j]++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // Loop the segments, find out dead segments.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    eorg = sorg(segloop);
    edest = sdest(segloop);
    spivot(segloop, parentsh);
    spivot(parentsh, neighsh);
    spivot(neighsh, neineighsh);
    if (parentsh.sh != neighsh.sh && parentsh.sh == neineighsh.sh) {
      // Exactly two subfaces at this segment.
      fidx1 = shellmark(parentsh) - 1;
      fidx2 = shellmark(neighsh) - 1;
      pbcflag = false;
      if (checkpbcs) {
        pbcflag = (shellpbcgroup(parentsh) >= 0)
          || (shellpbcgroup(neighsh) >= 0);
      }
      // Possibly merge them if they are not in the same facet.
      if ((fidx1 != fidx2) && !pbcflag) {
        // Test if they are coplanar.
        ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh));
        if (ori != 0.0) {
          if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori,
                         b->epsilon)) {
            ori = 0.0; // They are assumed as coplanar.
          }
        }
        if (ori == 0.0) {
          mergeflag = (in->facetmarkerlist == (int *) NULL || 
          in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]);
          if (mergeflag) {
            // This segment becomes dead.
            if (b->verbose > 1) {
              printf("  Removing segment (%d, %d).\n", pointmark(eorg),
                     pointmark(edest));
            }
            ssdissolve(parentsh);
            ssdissolve(neighsh);
            shellfacedealloc(subsegs, segloop.sh);
            j = pointmark(eorg);
            segspernodelist[j]--;
            if (segspernodelist[j] == 0) {
              setpointtype(eorg, FREESUBVERTEX);
            }
            j = pointmark(edest);
            segspernodelist[j]--;
            if (segspernodelist[j] == 0) {
              setpointtype(edest, FREESUBVERTEX);
            }
            // Add 'parentsh' to queue checking for flip.
            enqueueflipedge(parentsh, flipqueue);
          }
        }
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  if (!flipqueue->empty()) {
    // Restore the Delaunay property in the facet triangulation.
    flipsub(flipqueue);
  }

  delete [] segspernodelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// meshsurface()    Create the surface mesh of a PLC.                        //
//                                                                           //
// Let X be the PLC, the surface mesh S of X consists of triangulated facets.//
// S is created mainly in the following steps:                               //
//                                                                           //
// (1) Form the CDT of each facet of X separately (by routine triangulate()).//
// After it is done, the subfaces of each facet are connected to each other, //
// however there is no connection between facets yet.  Notice each facet has //
// its own segments, some of them are duplicated.                            //
//                                                                           //
// (2) Remove the redundant segments created in step (1) (by routine unify-  //
// segment()). The subface ring of each segment is created,  the connection  //
// between facets are established as well.                                   //
//                                                                           //
// The return value indicates the number of segments of X.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::meshsurface()
{
  list *ptlist, *conlist;
  queue *flipqueue;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  memorypool *viri;
  point *idx2verlist;
  point tstart, tend, *cons;
  int *worklist;
  int end1, end2;
  int shmark, i, j;

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

  // Compute a mapping from indices to points.
  makeindex2pointmap(idx2verlist);
  // Compute a mapping from points to tets for computing abovepoints.
  makepoint2tetmap();
  // Initialize 'facetabovepointarray'.
  facetabovepointarray = new point[in->numberoffacets + 1];
  for (i = 0; i < in->numberoffacets + 1; i++) {
    facetabovepointarray[i] = (point) NULL;
  }
  if (checkpbcs) {
    // Initialize the global array 'subpbcgrouptable'.
    createsubpbcgrouptable();
  }

  // Initialize working lists.
  viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0);
  flipqueue = new queue(sizeof(badface));
  ptlist = new list(sizeof(point *), NULL, 256);
  conlist = new list(sizeof(point *) * 2, NULL, 256);
  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 0;
  
  // Loop the facet list, triangulate each facet. On finish, all subfaces
  //   are in 'subfaces', all segments are in 'subsegs'. Notice: there're
  //   redundant segments.  Remember: All facet indices count from 1.
  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 by incrflipdelaunay().  Let p and q are dup.
    //   and the index of p is larger than q's, p is substituted by q.
    //   In a STL mesh, duplicated points are implicitly included.
    if ((b->object == tetgenbehavior::STL) || dupverts) {
      // 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 - in->firstnumber];
          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 V of vertices and S of 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 - in->firstnumber];
      // Add tstart to V if it haven't been added yet.
      if (worklist[end1] == 0) {
        ptlist->append(&tstart);
        worklist[end1] = 1;
      }
      // 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 - in->firstnumber];
            // Add tstart to V if it haven't been added yet.
            if (worklist[end2] == 0) {
              ptlist->append(&tend);
              worklist[end2] = 1;
            }
            // Save the segment in S (conlist).
            cons = (point *) conlist->append(NULL);
            cons[0] = tstart;
            cons[1] = tend;
            // Set the start for next continuous segment.
            end1 = end2;
            tstart = tend;
          } else {
            // Two identical vertices represent 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->len(); i++) {
      tstart = * (point *)(* ptlist)[i];
      end1 = pointmark(tstart);
      assert(worklist[end1] == 1);
      worklist[end1] = 0;
    }

    // Create a CDT of F.
    triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes,
                f->holelist, viri, flipqueue);
    // Clear working lists.
    ptlist->clear();
    conlist->clear();
    viri->restart();
  }

  // Unify segments in 'subsegs', remove redundant segments.  Face links
  //   of segments are also built.
  unifysegments();
  // Remember the number of input segments (for output).
  insegments = subsegs->items;

  if (checkpbcs) {
    // Create the global array 'segpbcgrouptable'.
    createsegpbcgrouptable();
  }

  if (b->object == tetgenbehavior::STL) {
    // Remove redundant vertices (for .stl input mesh).
    jettisonnodes();
  }

  if (!b->nomerge && !b->nobisect && !checkpbcs) {
    // No '-M' switch - merge adjacent facets if they are coplanar.
    mergefacets(flipqueue);
  }

  delete [] idx2verlist;
  delete [] worklist;
  delete ptlist;
  delete conlist;
  delete flipqueue;
  delete viri;

  return subsegs->items;
}

//
// End of surface triangulation routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interecursive()    Recursively do intersection test on a set of triangles.//
//                                                                           //
// Recursively split the set 'subfacearray' of subfaces into two sets using  //
// a cut plane parallel to x-, or, y-, or z-axies.  The split criteria are   //
// follows. Assume the cut plane is H, and H+ denotes the left halfspace of  //
// H, and H- denotes the right halfspace of H; and s be a subface:           //
//                                                                           //
//    (1) If all points of s lie at H+, put it into left array;              //
//    (2) If all points of s lie at H-, put it into right array;             //
//    (3) If some points of s lie at H+ and some of lie at H-, or some       //
//        points lie on H, put it into both arraies.                         //
//                                                                           //
// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis  //
// if axis == '2'. If current cut plane is parallel to the x-axis, the next  //
// one will be parallel to y-axis, and the next one after the next is z-axis,//
// and then alternately return back to x-axis.                               //
//                                                                           //
// Stop splitting when the number of triangles of the input array is not     //
// decreased anymore. Do tests on the current set.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin,
              REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax,
              int* internum)
{
  shellface **leftarray, **rightarray;
  face sface1, sface2;
  point p1, p2, p3;
  point p4, p5, p6;
  enum interresult intersect;
  REAL split;
  bool toleft, toright;
  int leftsize, rightsize;
  int i, j;

  if (b->verbose > 1) {
    printf("  Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
           arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
           axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
  }
    
  leftarray = new shellface*[arraysize];
  if (leftarray == NULL) {
    printf("Error in interecursive():  Insufficient memory.\n");
    terminatetetgen(1);
  }
  rightarray = new shellface*[arraysize];
  if (rightarray == NULL) {
    printf("Error in interecursive():  Insufficient memory.\n");
    terminatetetgen(1);
  }
  leftsize = rightsize = 0;

  if (axis == 0) {
    // Split along x-axis.
    split = 0.5 * (bxmin + bxmax);
  } else if (axis == 1) {
    // Split along y-axis.
    split = 0.5 * (bymin + bymax);
  } else {
    // Split along z-axis.
    split = 0.5 * (bzmin + bzmax);
  }

  for (i = 0; i < arraysize; i++) {
    sface1.sh = subfacearray[i];
    p1 = (point) sface1.sh[3];
    p2 = (point) sface1.sh[4];
    p3 = (point) sface1.sh[5];
    toleft = toright = false;
    if (p1[axis] < split) {
      toleft = true;
      if (p2[axis] >= split || p3[axis] >= split) {
        toright = true;
      } 
    } else if (p1[axis] > split) {
      toright = true;
      if (p2[axis] <= split || p3[axis] <= split) {
        toleft = true;
      } 
    } else {
      // p1[axis] == split;
      toleft = true;
      toright = true;
    }
    // At least one is true;
#ifdef SELF_CHECK
    assert(!(toleft == false && toright == false));
#endif
    if (toleft) {
      leftarray[leftsize] = sface1.sh;
      leftsize++;
    }
    if (toright) {
      rightarray[rightsize] = sface1.sh;
      rightsize++;
    }
  }

  if (leftsize < arraysize && rightsize < arraysize) {
    // Continue to partition the input set. Now 'subfacearray' has been
    //   split into two sets, it's memory can be freed. 'leftarray' and
    //   'rightarray' will be freed in the next recursive (after they're
    //   partitioned again or performing tests).
    delete [] subfacearray;
    // Continue to split these two sets.
    if (axis == 0) {
      interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax,
                    bzmin, bzmax, internum);
    } else if (axis == 1) {
      interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax,
                    bzmin, bzmax, internum);
    } else {
      interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax,
                    bzmin, split, internum);
      interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax,
                    split, bzmax, internum);
    }
  } else {
    if (b->verbose > 1) {
      printf("  Checking intersecting faces.\n");
    }
    // Perform a brute-force compare on the set.
    for (i = 0; i < arraysize; i++) {
      sface1.sh = subfacearray[i];
      p1 = (point) sface1.sh[3];
      p2 = (point) sface1.sh[4];
      p3 = (point) sface1.sh[5];
      for (j = i + 1; j < arraysize; j++) {
        sface2.sh = subfacearray[j];
        p4 = (point) sface2.sh[3];
        p5 = (point) sface2.sh[4];
        p6 = (point) sface2.sh[5];
        intersect = tri_tri_inter(p1, p2, p3, p4, p5, p6);
        if (intersect == INTERSECT || intersect == SHAREFACE) {
          if (!b->quiet) {
            if (intersect == INTERSECT) {
              printf("  Facet #%d intersects facet #%d at triangles:\n",
                     shellmark(sface1), shellmark(sface2));
              printf("    (%4d, %4d, %4d) and (%4d, %4d, %4d)\n",
                     pointmark(p1), pointmark(p2), pointmark(p3),
                     pointmark(p4), pointmark(p5), pointmark(p6));
            } else {
              printf("  Facet #%d duplicates facet #%d at triangle:\n",
                     shellmark(sface1), shellmark(sface2));
              printf("    (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2),
                     pointmark(p3));
            }
          }
          // Increase the number of intersecting pairs.
          (*internum)++; 
          // Infect these two faces (although they may already be infected).
          sinfect(sface1);
          sinfect(sface2);
        }
      }
    }
    // Don't forget to free all three arrays. No further partition.
    delete [] leftarray;
    delete [] rightarray;  
    delete [] subfacearray;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// detectinterfaces()    Detect intersecting triangles.                      //
//                                                                           //
// Given a set of triangles,  find the pairs of intersecting triangles from  //
// them.  Here the set of triangles is in 'subfaces' which is a surface mesh //
// of a PLC (.poly or .smesh).                                               //
//                                                                           //
// To detect whether two triangles are intersecting is done by the routine   //
// 'tri_tri_inter()'.  The algorithm for the test is very simple and stable. //
// It is based on geometric orientation test which uses exact arithmetics.   //
//                                                                           //
// Use divide-and-conquer algorithm for reducing the number of intersection  //
// tests.  Start from the bounding box of the input point set, recursively   //
// partition the box into smaller boxes, until the number of triangles in a  //
// box is not decreased anymore. Then perform triangle-triangle tests on the //
// remaining set of triangles.  The memory allocated in the input set is     //
// freed immediately after it has been partitioned into two arrays.  So it   //
// can be re-used for the consequent partitions.                             //
//                                                                           //
// On return, the pool 'subfaces' will be cleared, and only the intersecting //
// triangles remain for output (to a .face file).                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::detectinterfaces()
{
  shellface **subfacearray;
  face shloop;
  int internum;
  int i;

  if (!b->quiet) {
    printf("Detecting intersecting facets.\n");
  }

  // Construct a map from indices to subfaces;
  subfacearray = new shellface*[subfaces->items];
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  i = 0;
  while (shloop.sh != (shellface *) NULL) {
    subfacearray[i] = shloop.sh;
    shloop.sh = shellfacetraverse(subfaces);
    i++;
  }

  internum = 0;
  // Recursively split the set of triangles into two sets using a cut plane
  //   parallel to x-, or, y-, or z-axies.  Stop splitting when the number
  //   of subfaces is not decreasing anymore. Do tests on the current set.
  interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax,
                zmin, zmax, &internum);

  if (!b->quiet) {
    if (internum > 0) {
      printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum);
    } else {
      printf("\nNo faces are intersecting.\n\n");
    }
  }

  if (internum > 0) {
    // Traverse all subfaces, deallocate those have not been infected (they
    //   are not intersecting faces). Uninfect those have been infected.
    //   After this loop, only intersecting faces remain.
    subfaces->traversalinit();
    shloop.sh = shellfacetraverse(subfaces);
    while (shloop.sh != (shellface *) NULL) {
      if (sinfected(shloop)) {
        suninfect(shloop);
      } else {
        shellfacedealloc(subfaces, shloop.sh);
      }
      shloop.sh = shellfacetraverse(subfaces);
    }
  } else {
    // Deallocate all subfaces.
    subfaces->restart();
  }
}

//
// Begin of periodic boundary condition routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// createsubpbcgrouptable()    Create the 'subpbcgrouptable'.                //
//                                                                           //
// Allocate the memory for 'subpbcgrouptable'.  Each entry i (a pbcdata) of  //
// the table represents a pbcgroup.  Most of the fields of a group-i are set //
// in this routine. 'fmark[0]', 'fmark[1]', and 'transmat[0]' are directly   //
// copied from the corresponding data of 'in->numberofpbcgroups'. 'transmat  //
// [1]' is calculated as the inverse matrix of 'transmat[0]'.  'ss[0]' and   //
// 'ss[1]' are initilized be 'dummysh'. They are set in 'trangulatefacet()'  //
// (when -p is in use) or 'reconstructmesh()' (when -r is in use).           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::createsubpbcgrouptable()
{
  tetgenio::pbcgroup *pg;
  pbcdata *pd;
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i, j, k;

  subpbcgrouptable = new pbcdata[in->numberofpbcgroups];
  for (i = 0; i < in->numberofpbcgroups; i++) {
    pg = &(in->pbcgrouplist[i]);
    pd = &(subpbcgrouptable[i]);
    // Copy data from pg to pd.
    pd->fmark[0] = pg->fmark1;
    pd->fmark[1] = pg->fmark2;
    // Initialize array 'pd->ss'.
    pd->ss[0].sh = dummysh;
    pd->ss[1].sh = dummysh;
    // Copy the transform matrix from pg to pd->transmat[0].
    for (j = 0; j < 4; j++) {
      for (k = 0; k < 4; k++) {
        pd->transmat[0][j][k] = pg->transmat[j][k];
        // Prepare for inverting the matrix.
        A[j][k] = pg->transmat[j][k];
      }
    }
    // Calculate the inverse matrix (pd->transmat[1]) of pd->transmat[0].
    lu_decmp(A, 4, indx, &D, 0);
    for (j = 0; j < 4; j++) {
      for (k = 0; k < 4; k++) rhs[k] = 0.0;
      rhs[j] = 1.0;
      lu_solve(A, 4, indx, rhs, 0);
      for (k = 0; k < 4; k++) pd->transmat[1][k][j] = rhs[k];
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubpbcgroup()    Get the pbcgroup of a subface.                        //
//                                                                           //
// 'pbcsub' has pbc defined. Its pbcgroup is returned in 'pd'. In addition,  //
// 'f1' (0 or 1) indicates the position of 'pbcsub' in 'pd'; 'f2' (= 1 - f1) //
// is the position where the symmetric subface of 'pbcsub' is found.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsubpbcgroup(face* pbcsub, pbcdata** pd, int *f1, int *f2)
{
  int groupid, fmark, idx;

  groupid = shellpbcgroup(*pbcsub);
  *pd = &subpbcgrouptable[groupid];
  
  // Get the facet index (1 - based).
  idx = shellmark(*pbcsub);
  // Get the facet marker from array (0 - based).
  fmark = in->facetmarkerlist[idx - 1];
  if ((*pd)->fmark[0] == fmark) {
    *f1 = 0;
  } else {
#ifdef SELF_CHECK
    assert((*pd)->fmark[1] == fmark);
#endif
    *f1 = 1;
  }
  *f2 = 1 - (*f1);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubpbcsympoint()    Compute the symmetric point for a subface point.   //
//                                                                           //
// 'newpoint' lies on 'splitsub'. This routine calculates a 'sympoint' which //
// locates on 'symsplitsub' and symmtric to 'newpoint'.  Return the location //
// of sympoint wrt. symsplitsub.                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh:: getsubpbcsympoint(point newpoint,
  face* splitsub, point sympoint, face* symsplitsub)
{
  pbcdata *pd;
  face subloop;
  point pa, pb, pc;
  enum locateresult symloc;
  REAL ori;
  int f1, f2, i;

  // Get the pbcgroup of 'splitsub'.
  getsubpbcgroup(splitsub, &pd, &f1, &f2);
      
  // Transform newpoint from f1 -> f2.
  for (i = 0; i < 3; i++) {
    sympoint[i] = pd->transmat[f1][i][0] * newpoint[0]
                + pd->transmat[f1][i][1] * newpoint[1]
                + pd->transmat[f1][i][2] * newpoint[2]
                + pd->transmat[f1][i][3] * 1.0;
  }
  // Locate sympoint in f2.
  symloc = OUTSIDE;
  *symsplitsub = pd->ss[f2];
  // Is the stored subface valid? Hole removal may delete the subface.  
  if ((symsplitsub->sh != dummysh) && !isdead(symsplitsub)) {
    // 'symsplitsub' should lie on the symmetric facet. Check it.
    i = shellmark(*symsplitsub);
    if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) {
      // 'symsplitsub' has the symmetric boundary marker.
      pa = sorg(*symsplitsub);
      pb = sdest(*symsplitsub);
      pc = sapex(*symsplitsub);
      // Test if they are (nearly) coplanar. Some facets may have the
      //   same boundary marker but not coplanar with this point.
      ori = orient3d(pa, pb, pc, sympoint);
      if (iscoplanar(pa, pb, pc, sympoint, ori, b->epsilon * 1e+2)) {
        // Locate sympoint in facet. Don't stop at subsegment.
        abovepoint = facetabovepointarray[shellmark(*symsplitsub)];
        if (abovepoint == (point) NULL) {
          getfacetabovepoint(symsplitsub);
        }
        symloc = locatesub(sympoint, symsplitsub, 0, b->epsilon * 1e+2);
      }
    }
  }
  if (symloc == OUTSIDE) {
    // Do a brute-force searching for the symmetric subface.
    REAL epspp = b->epsilon * 1e+2;
    int lcount = 0;
    do {
      // Locate sympoint in the pool of subfaces (with fmark pd->fmark[f2]).
      subfaces->traversalinit();
      subloop.sh = shellfacetraverse(subfaces);
      while (subloop.sh != (shellface *) NULL) {
        i = shellmark(subloop);
        if (in->facetmarkerlist[i - 1] == pd->fmark[f2]) {
          // Found a facet have the symmetric boundary marker.
          pa = sorg(subloop);
          pb = sdest(subloop);
          pc = sapex(subloop);
          // Test if they are (nearly) coplanar. Some facets may have the
          //   same boundary marker but not coplanar with this point.
          ori = orient3d(pa, pb, pc, sympoint);
          if (iscoplanar(pa, pb, pc, sympoint, ori, epspp)) {
            // Test if sympoint is (nearly) inside this facet. 
            // Get the abovepoint of the facet.
            abovepoint = facetabovepointarray[shellmark(subloop)];
            // Do we need to calculate the abovepoint?
            if (abovepoint == (point) NULL) {
              getfacetabovepoint(&subloop);
            }
            // subloop is on the facet, search sympoint.
            symloc = locatesub(sympoint, &subloop, 0, epspp);
            if (symloc != OUTSIDE) break;
          }
        }
        subloop.sh = shellfacetraverse(subfaces);
      }
      lcount++;
      epspp *= 10.0;
    } while ((symloc == OUTSIDE) && (lcount < 3));
#ifdef SELF_CHECK
    // sympoint should be inside the facet.
    assert(symloc != OUTSIDE);
#endif
    // Set the returning subface.
    *symsplitsub = subloop;
    // Update the stored subface for next searching.
    pd->ss[f2] = *symsplitsub;
  }

  return adjustlocatesub(sympoint, symsplitsub, symloc, b->epsilon);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// createsegpbcgrouptable()    Create the 'segpbcgrouptable'.                //
//                                                                           //
// Each segment may belong to more than one pbcgroups.  For example, segment //
// ab may need to be symmteric to both segments cd, and ef, then  ab and cd, //
// cd and ef, ef and ab form three pbcgroups.                                //
//                                                                           //
// 'segpbcgrouptable' is  implemented as a list of pbcdatas. Each item i is  //
// a pbcgroup.                                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::createsegpbcgrouptable()
{
  shellface** segsperverlist;
  pbcdata *pd, *ppd, pd1, pd2;
  face segloop, symseg;
  face startsh, spinsh, symsh;
  point pa, pb, syma, symb;
  enum locateresult symloc;
  REAL testpt[3], sympt[3];
  bool inflag;
  int *idx2seglist;
  int segid1, segid2;
  int f1, f2;
  int i, j, k, l;

  // Allocate memory for 'subpbcgrouptable'.
  segpbcgrouptable = new list(sizeof(pbcdata), NULL, 256);

  if (b->refine) {
    // Create a point-to-seg map for quickly finding PBC seg pairs.
    makesegmentmap(idx2seglist, segsperverlist);
  }

  // Loop through the segment list.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Loop the subface ring of segloop ab.
    pa = sorg(segloop);
    pb = sdest(segloop);
    segid1 = shellmark(segloop);
    spivot(segloop, startsh);
    spinsh = startsh;
    do {
      // Adjust spinsh be edge ab.
      if (sorg(spinsh) != pa) {
        sesymself(spinsh);
      }
      // Does spinsh belong to a pbcgroup?
      if (shellpbcgroup(spinsh) != -1) {
        // Yes! There exists a segment cd. ab and cd form a pbcgroup.
        if (b->refine) {
          getsubpbcgroup(&spinsh, &pd, &f1, &f2);
          // Transform pa from f1 -> f2.
          for (i = 0; i < 3; i++) {
            sympt[i] = pd->transmat[f1][i][0] * pa[0]
                     + pd->transmat[f1][i][1] * pa[1]
                     + pd->transmat[f1][i][2] * pa[2]
                     + pd->transmat[f1][i][3] * 1.0;
          }
          syma = point2pbcpt(pa);
          // Is 'sympt == syma'?
          if (distance(sympt, syma) > (longest * b->epsilon)) {
            // No. Search the symmetric vertex of pa.
            symloc = getsubpbcsympoint(pa, &spinsh, sympt, &symsh);
            syma = sorg(symsh);
            if (symloc != ONVERTEX) {
              // Do a brute force search. Not done yet.
              assert(0);
            }
          }
          // Transform pb from f1 -> f2.
          for (i = 0; i < 3; i++) {
            sympt[i] = pd->transmat[f1][i][0] * pb[0]
                     + pd->transmat[f1][i][1] * pb[1]
                     + pd->transmat[f1][i][2] * pb[2]
                     + pd->transmat[f1][i][3] * 1.0;
          }
          // Search sym subface from the point-to-subface map.
          symseg.shver = 0;
          j = pointmark(syma) - in->firstnumber;
          for (i = idx2seglist[j]; i < idx2seglist[j + 1]; i++) {
            symseg.sh = segsperverlist[i];
            if (sorg(symseg) == syma) symb = sdest(symseg);
            else symb = sorg(symseg);
            if (distance(sympt, symb) <= (longest * b->epsilon)) break;
          }
          assert(i < idx2seglist[j + 1]);
        } else {
          //   'testpt' is the midpoint of ab used to find cd.
          for (i = 0; i < 3; i++) testpt[i] = 0.5 * (pa[i] + pb[i]);
          symloc = getsubpbcsympoint(testpt, &spinsh, sympt, &symsh);
#ifdef SELF_CHECK
          assert(symloc == ONEDGE);
#endif
          sspivot(symsh, symseg);
        }
#ifdef SELF_CHECK
        assert(symseg.sh != dummysh);
#endif
        // Check whether this group has already been created in list.
        segid2 = shellmark(symseg);
        inflag = false;
        for (i = 0; i < segpbcgrouptable->len() && !inflag; i++) {
          pd = (pbcdata *)(* segpbcgrouptable)[i];
          if (pd->segid[0] == segid1) {
            if (pd->segid[1] == segid2) inflag = true;
          } else if (pd->segid[0] == segid2) {
            if (pd->segid[1] == segid1) inflag = true;
          }
        }
        if (!inflag) {
          // Create a segment pbcgroup in list for ab and cd.
          pd = (pbcdata *) segpbcgrouptable->append(NULL);
          // Save the markers of ab and cd.
          pd->segid[0] = segid1;
          pd->segid[1] = segid2;
          // Save the handles of ab and cd.
          pd->ss[0] = segloop;
          pd->ss[1] = symseg;
          // Find the map from ab to cd.
          getsubpbcgroup(&spinsh, &ppd, &f1, &f2);
          pd->fmark[0] = ppd->fmark[f1];
          pd->fmark[1] = ppd->fmark[f2];
          // Set the map from ab to cd.
          for (i = 0; i < 4; i++) {
            for (j = 0; j < 4; j++) {
              pd->transmat[0][i][j] = ppd->transmat[f1][i][j];
            }
          }
          // Set the map from cd to ab.
          for (i = 0; i < 4; i++) {
            for (j = 0; j < 4; j++) {
              pd->transmat[1][i][j] = ppd->transmat[f2][i][j];
            }
          }
        }
      }
      // Go to the next subface in the ring of ab.
      spivotself(spinsh);
    } while (spinsh.sh != startsh.sh);
    segloop.sh = shellfacetraverse(subsegs);
  }
  
  if (b->refine) {
    delete [] segsperverlist;
    delete [] idx2seglist;
  }

  // Create the indirect segment pbcgroups.
  // Bug-fixed (08 Sept. 2006). The total size of 'segpbcgrouptable' may get
  //   increased. Do not use pointers for 'pd1' and 'pd2'. The addresses may
  //   be invaild after realloc().
  for (i = 0; i < segpbcgrouptable->len(); i++) {
    pd1 = * (pbcdata *)(* segpbcgrouptable)[i];
    for (f1 = 0; f1 < 2; f1++) {
      // Search for a group (except i) contains pd1.segid[f1].
      for (j = 0; j < segpbcgrouptable->len(); j++) {
        if (j == i) continue;
        pd2 = * (pbcdata *)(* segpbcgrouptable)[j];
        f2 = -1;
        if (pd1.segid[f1] == pd2.segid[0]) {
          f2 = 0;
        } else if (pd1.segid[f1] == pd2.segid[1]) {
          f2 = 1;
        }
        if (f2 != -1) {
#ifdef SELF_CHECK
          assert(pd1.segid[f1] == pd2.segid[f2]);
#endif
          segid1 = pd1.segid[1 - f1];
          segid2 = pd2.segid[1 - f2];
          // Search for the existence of segment pbcgroup (segid1, segid2).
          inflag = false;
          for (k = 0; k < segpbcgrouptable->len() && !inflag; k++) {
            pd = (pbcdata *)(* segpbcgrouptable)[k];
            if (pd->segid[0] == segid1) {
              if (pd->segid[1] == segid2) inflag = true;
            } else if (pd->segid[0] == segid2) {
              if (pd->segid[1] == segid1) inflag = true;
            }
          }
          if (!inflag) {
            pd = (pbcdata *) segpbcgrouptable->append(NULL);
            pd->segid[0] = pd1.segid[1 - f1];
            pd->segid[1] = pd2.segid[1 - f2];
            pd->ss[0] = pd1.ss[1 - f1];
            pd->ss[1] = pd2.ss[1 - f2];
            // Invalid the fmark[0] == fmark[1].
            pd->fmark[0] = pd->fmark[1] = 0;
            // Translate matrix pd->transmat[0] = m2 * m1, where m1 =
            //   pd1.transmat[1 - f1], m2 = pd2.transmat[f2].
            for (k = 0; k < 4; k++) {
              for (l = 0; l < 4; l++) { 
                pd->transmat[0][k][l] = pd2.transmat[f2][k][l];
              }
            }
            m4xm4(pd->transmat[0], pd1.transmat[1 - f1]);
            // Translate matrix pd->transmat[1] = m4 * m3, where m3 =
            //   pd2.transmat[1 - f2], m4 = pd1.transmat[f1].
            for (k = 0; k < 4; k++) {
              for (l = 0; l < 4; l++) { 
                pd->transmat[1][k][l] = pd1.transmat[f1][k][l];
              }
            }
            m4xm4(pd->transmat[1], pd2.transmat[1 - f2]);
          }
        }
      }
    }
  }

  // Form a map from segment index to pbcgroup list of this segment.
  idx2segpglist = new int[subsegs->items + 1];
  for (i = 0; i < subsegs->items + 1; i++) idx2segpglist[i] = 0;
  // Loop through 'segpbcgrouptable', counter the number of pbcgroups of
  //   each segment.
  for (i = 0; i < segpbcgrouptable->len(); i++) {
    pd = (pbcdata *)(* segpbcgrouptable)[i];
    for (j = 0; j < 2; j++) {
      k = pd->segid[j] - 1;
      idx2segpglist[k]++;
    }
  }
  // Calculate the total length of array 'segpglist'.
  j = idx2segpglist[0];
  idx2segpglist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < subsegs->items; i++) {
    k = idx2segpglist[i + 1];
    idx2segpglist[i + 1] = idx2segpglist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2segpglist.
  segpglist = new int[idx2segpglist[i]];
  // Loop the set of pbcgroups again, set the data into segpglist.
  for (i = 0; i < segpbcgrouptable->len(); i++) {
    pd = (pbcdata *)(* segpbcgrouptable)[i];
    for (j = 0; j < 2; j++) {
      k = pd->segid[j] - 1;
      segpglist[idx2segpglist[k]] = i;
      idx2segpglist[k]++;
    }
  }
  // Contents in 'idx2segpglist' are shifted, now shift them back.
  for (i = subsegs->items - 1; i >= 0; i--) {
    idx2segpglist[i + 1] = idx2segpglist[i];
  }
  idx2segpglist[0] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsegpbcsympoint()    Compute the symmetric point for a segment point.   //
//                                                                           //
// 'newpoint' lies on 'splitseg'. This routine calculates a 'sympoint' which //
// locates on 'symsplitseg' and symmtric to 'newpoint'.  Return the location //
// of sympoint wrt. symsplitseg.                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
getsegpbcsympoint(point newpoint, face* splitseg, point sympoint,
                  face* symsplitseg, int groupid)
{
  pbcdata *pd;
  enum locateresult symloc;
  int segid, f1, f2, i;

  pd = (pbcdata *)(* segpbcgrouptable)[groupid];
  segid = shellmark(*splitseg);
  if (pd->segid[0] == segid) {
    f1 = 0;
  } else {
#ifdef SELF_CHECK
    assert(pd->segid[1] == segid);
#endif
    f1 = 1;
  }
  f2 = 1 - f1;

  // Transform newpoint from f1 -> f2.
  for (i = 0; i < 3; i++) {
    sympoint[i] = pd->transmat[f1][i][0] * newpoint[0]
                + pd->transmat[f1][i][1] * newpoint[1]
                + pd->transmat[f1][i][2] * newpoint[2]
                + pd->transmat[f1][i][3] * 1.0;
  }
  // Locate sympoint in f2.
  *symsplitseg = pd->ss[f2];
#ifdef SELF_CHECK
  assert(symsplitseg->sh != dummysh);
#endif
  // Locate sympoint in facet. Stop at subsegment.
  symloc = locateseg(sympoint, symsplitseg);
  symloc = adjustlocateseg(sympoint, symsplitseg, symloc, b->epsilon * 1e+2);
  return symloc;
}

//
// End of periodic boundary condition routines
//

//
// Begin of vertex perturbation routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// randgenerator()    Generate a random REAL number between (0, |range|).    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::randgenerator(REAL range)
{
  REAL worknumber, result;
  int expo;

  if (range == 0.0) return 0.0;

  expo = 0;
  worknumber = fabs(range);
  // Normalize worknumber (i.e., 1.xxxExx)
  if (worknumber > 10.0) {
    while (worknumber > 10.0) {
      worknumber /= 10.0;
      expo++;
    }
  } else if (worknumber < 1.0) {
    while (worknumber < 1.0) {
      worknumber *= 10.0;
      expo--;
    }
  }
#ifdef SELF_CHECK
  assert(worknumber >= 1.0 && worknumber <= 10.0);
#endif

  // Enlarge worknumber 1000 times.
  worknumber *= 1e+3;
  expo -= 3;
  // Generate a randome number between (0, worknumber).
  result = (double) randomnation((int) worknumber);
  
  // Scale result back into the original size.
  if (expo > 0) {
    while (expo != 0) {
      result *= 10.0;
      expo--;
    }
  } else if (expo < 0) {
    while (expo != 0) {
      result /= 10.0;
      expo++;
    }
  }
#ifdef SELF_CHECK
  assert((result >= 0.0) && (result <= fabs(range)));
#endif

  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checksub4cocir()    Test a subface to find co-circular pair of subfaces.  //
//                                                                           //
// 'eps' is a relative tolerance for testing approximately cospherical case. //
// Set it to zero if only exact test is desired.                             //
//                                                                           //
// An edge(not a segment) of 'testsub' is locally degenerate if the opposite //
// vertex of the adjacent subface is cocircular with the vertices of testsub.//
// If 'once' is TRUE, operate on the edge only if the pointer 'testsub->sh'  //
// is smaller than its neighbor (for each edge is considered only once).     //
//                                                                           //
// Return TRUE if find an edge of testsub is locally degenerate.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checksub4cocir(face* testsub, REAL eps, bool once,
  bool enqflag)
{
  badface *cocirsub;
  face subloop, neighsub;
  face checkseg;
  point pa, pb, pc, pd;
  REAL sign;
  int i;
  
  subloop = *testsub;
  subloop.shver = 0; // Keep the CCW orientation.
  // Get the abovepoint of the facet.
  abovepoint = facetabovepointarray[shellmark(subloop)];
  // Do we need to calculate the abovepoint?
  if (abovepoint == (point) NULL) {
    getfacetabovepoint(&subloop);
  }
  // Check the three edges of subloop.
  for (i = 0; i < 3; i++) {
    sspivot(subloop, checkseg);
    if (checkseg.sh == dummysh) {
      // It is not a segment, get the adjacent subface.
      spivot(subloop, neighsub);
      // assert(neighsub.sh != dummysh);
      if (!once || (once && (neighsub.sh > subloop.sh))) {
        pa = sorg(subloop);
        pb = sdest(subloop);
        pc = sapex(subloop);
        pd = sapex(neighsub);
        sign = insphere(pa, pb, pc, abovepoint, pd);
        if ((sign != 0.0) && (eps > 0.0)) {
          if (iscospheric(pa, pb, pc, abovepoint, pd, sign, eps)) sign = 0.0;
        }
        if (sign == 0.0) {
          // It's locally degenerate!
          if (enqflag && badsubfaces != (memorypool *) NULL) {
            // Save it.
            cocirsub = (badface *) badsubfaces->alloc();
            cocirsub->ss = subloop;
            cocirsub->forg = pa;
            cocirsub->fdest = pb;
            cocirsub->fapex = pc;
            cocirsub->foppo = pd;
            setshell2badface(cocirsub->ss, cocirsub);
          }
          if (b->verbose > 1) {
            printf("    Found set (%d, %d, %d, %d).\n", pointmark(pa),
                   pointmark(pb), pointmark(pc), pointmark(pd));
          }
          return true;
        }
      }
    }
    senextself(subloop);
  }

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallcocirsubs()    Find all co-circular subfaces and save them in list.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallcocirsubs(REAL eps, bool enqflag)
{
  face subloop;

  // Loop over all subfaces.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    checksub4cocir(&subloop, eps, true, enqflag);
    subloop.sh = shellfacetraverse(subfaces);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallencsegsfsubs()    Check for encroached segs from a list of subfaces.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tallencsegsfsubs(point testpt, list* cavsublist)
{
  face startsub, checkseg;
  long oldencnum;
  int i, j;

  // Remember the current number of encroached segments.
  oldencnum = badsubsegs->items;

  // Check segments in the list of subfaces.
  for (i = 0; i < cavsublist->len(); i++) {
    startsub = * (face *)(* cavsublist)[i];
    // Test all three edges of startsub.
    for (j = 0; j < 3; j++) {
      sspivot(startsub, checkseg);
      if (checkseg.sh != dummysh) {
        if (!shell2badface(checkseg)) {
          checkseg4encroach(&checkseg, testpt, NULL, true);
        }
      }
      senextself(startsub);
    }
  }

  return (badsubsegs->items > oldencnum);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// collectflipedges()    Collect edges of split subfaces for flip checking.  //
//                                                                           //
// 'inspoint' is a newly inserted segment point (inserted by insertsite()).  //
// 'splitseg' is one of the two split subsegments. Some subfaces may be non- //
// Delaunay since they're still not bonded to CDT. This routine collect all  //
// such possible subfaces in 'flipqueue'.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
collectflipedges(point inspoint, face* splitseg, queue* flipqueue)
{
  face startsh, spinsh, checksh;
  face nextseg;
  point pa, pb;

  // Let the dest of splitseg be inspoint.
  splitseg->shver = 0;
  if (sdest(*splitseg) != inspoint) {
    sesymself(*splitseg);
  }
#ifdef SELF_CHECK
  assert(sdest(*splitseg) == inspoint);
#endif
  pa = sorg(*splitseg);
  spivot(*splitseg, startsh);
  spinsh = startsh;
  do {
    findedge(&spinsh, pa, inspoint);
    senext2(spinsh, checksh);
    enqueueflipedge(checksh, flipqueue);
    spivotself(spinsh);
  } while (spinsh.sh != startsh.sh);

  // Get the next subsegment.
  senext(*splitseg, nextseg);
  spivotself(nextseg);
#ifdef SELF_CHECK
  assert(nextseg.sh != (shellface *) NULL);
#endif
  
  // Let the org of nextseg be inspoint.
  nextseg.shver = 0;
  if (sorg(nextseg) != inspoint) {
    sesymself(nextseg);
  }
#ifdef SELF_CHECK
  assert(sorg(nextseg) == inspoint);
#endif
  pb = sdest(nextseg);
  spivot(nextseg, startsh);
  spinsh = startsh;
  do {
    findedge(&spinsh, inspoint, pb);
    senext(spinsh, checksh);
    enqueueflipedge(checksh, flipqueue);
    spivotself(spinsh);
  } while (spinsh.sh != startsh.sh);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// perturbrepairencsegs()    Repair all encroached segments.                 //
//                                                                           //
// All encroached segments are stored in 'badsubsegs'.  Each segment will be //
// split by adding a perturbed point near its circumcenter.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::perturbrepairencsegs(queue* flipqueue)
{
  badface *encloop;
  tetrahedron encodedtet;
  triface splittet;
  face splitsub, symsplitsub;
  face splitseg, symsplitseg;
  point newpoint, sympoint;
  point pa, pb, pc;
  enum insertsiteresult success;
  enum locateresult loc, symloc;
  REAL cent[3], d1, ps, rs;
  int i, j;

  // Note that steinerleft == -1 if an unlimited number of Steiner points 
  //   is allowed.  Loop until 'badsubsegs' is empty.
  badsubsegs->traversalinit();
  encloop = badfacetraverse(badsubsegs);
  while ((encloop != (badface *) NULL) && (steinerleft != 0)) {
    splitseg = encloop->ss;
#ifdef SELF_CHECK
    assert(shell2badface(splitseg) == encloop);
#endif
    setshell2badface(splitseg, NULL);
    pa = sorg(splitseg);
    pb = sdest(splitseg);
    if ((pa == encloop->forg) && (pb == encloop->fdest)) {
      if (b->verbose > 1) {
        printf("  Get seg (%d, %d).\n", pointmark(pa), pointmark(pb));
      }
      // Create the newpoint.
      makepoint(&newpoint);
      // Get the circumcenter and radius of ab.
      for (i = 0; i < 3; i++) cent[i] = 0.5 * (pa[i] + pb[i]);
      d1 = 0.5 * distance(pa, pb);
      // Add a random perturbation to newpoint along the vector ab.
      ps = randgenerator(d1 * 1.0e-3);
      rs = ps / d1;
      // Set newpoint (be at the perturbed circumcenter of ab).
      for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]);
      setpointtype(newpoint, FREESEGVERTEX);
      // Set splitseg into the newpoint.
      setpoint2sh(newpoint, sencode(splitseg));        

      // Is there periodic boundary condition?
      if (checkpbcs) {
        // Insert points on other segments of incident pbcgroups.
        i = shellmark(splitseg) - 1;
        for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) {
          makepoint(&sympoint);
          symloc = getsegpbcsympoint(newpoint, &splitseg, sympoint,
                                     &symsplitseg, segpglist[j]);
#ifdef SELF_CHECK
          assert(symloc != OUTSIDE);
#endif
          // Note: the symsplitseg and splitseg may be identical, in case
          //   when the the splitseg is the axis of the rotational sym.
          if ((symloc == ONEDGE) && (symsplitseg.sh != splitseg.sh)) {
            setpointtype(sympoint, FREESEGVERTEX);
            setpoint2sh(sympoint, sencode(symsplitseg));
            // Insert sympoint into DT.
            pc = sorg(symsplitseg);
            splittet.tet = dummytet;
            // Find a good start point to search.
            encodedtet = point2tet(pc);
            if (encodedtet != (tetrahedron) NULL) {
              decode(encodedtet, splittet);
              if (isdead(&splittet)) {
                splittet.tet = dummytet; 
              }
            }
            // Locate sympoint in DT.  Do exact location.
            success = insertsite(sympoint, &splittet, false, flipqueue);
#ifdef SELF_CHECK
            assert(success != DUPLICATEPOINT);
#endif
            if (success == OUTSIDEPOINT) {
              inserthullsite(sympoint, &splittet, flipqueue);
            }
            if (steinerleft > 0) steinerleft--;
            // Let sympoint remember splittet.
            setpoint2tet(sympoint, encode(splittet));
            // Do flip in DT.
            flip(flipqueue, NULL);
            // Insert sympoint into F.
            symloc = locateseg(sympoint, &symsplitseg);
            if (symloc == ONEDGE) {
              symsplitseg.shver = 0;
              spivot(symsplitseg, symsplitsub);
              // sympoint should on the edge of symsplitsub.
              splitsubedge(sympoint, &symsplitsub, flipqueue);
            } else {
              // insertsite() has done the whole job.
#ifdef SELF_CHECK
              assert(symloc == ONVERTEX);
              assert(checksubfaces);
#endif
              // Some edges may need to be flipped.
              collectflipedges(sympoint, &symsplitseg, flipqueue);
            }
            // Do flip in facet.
            flipsub(flipqueue);
          } else { // if (symloc == ONVERTEX) {
            // The symmtric point already exists. It is possible when two
            //   pbc group are idebtical. Omit sympoint.
            pointdealloc(sympoint);
          }
        }
      }

      // Insert newpoint into DT.
      splittet.tet = dummytet;
      // Find a good start point to search.
      encodedtet = point2tet(pa);
      if (encodedtet != (tetrahedron) NULL) {
        decode(encodedtet, splittet);
        if (isdead(&splittet)) {
          splittet.tet = dummytet; 
        }
      }
      if (splittet.tet == dummytet) { // Try pb.
        encodedtet = point2tet(pb);
        if (encodedtet != (tetrahedron) NULL) {
          decode(encodedtet, splittet);
          if (isdead(&splittet)) {
            splittet.tet = dummytet;
          }
        }
      }
      // Locate the newpoint in DT.  Do exact location.
      success = insertsite(newpoint, &splittet, false, flipqueue);
#ifdef SELF_CHECK
      assert(success != DUPLICATEPOINT);
#endif
      if (success == OUTSIDEPOINT) {
        // A convex hull edge is mssing, and the inserting point lies
        //   (slightly) outside the convex hull due to the significant
        //   digits lost in the calculation. Enlarge the convex hull.
        inserthullsite(newpoint, &splittet, flipqueue);
      }
      if (steinerleft > 0) steinerleft--;
      // Let newpoint remember splittet.
      setpoint2tet(newpoint, encode(splittet));
      // Do flip in DT.
      flip(flipqueue, NULL);
      // Insert newpoint into F.
      loc = locateseg(newpoint, &splitseg);
      if (loc == ONEDGE) {
        splitseg.shver = 0;
        spivot(splitseg, splitsub);
        // newpoint should on the edge of splitsub.
        splitsubedge(newpoint, &splitsub, flipqueue);
      } else {
        // insertsite() has done the whole job.
#ifdef SELF_CHECK
        assert(loc == ONVERTEX);
        assert(checksubfaces);
#endif
        // Some edges may need to be flipped.
        collectflipedges(newpoint, &splitseg, flipqueue);
      }
      // Do flip in facet.
      flipsub(flipqueue);
    }
    // Remove this entry from list.
    badfacedealloc(badsubsegs, encloop);  
    // Get the next encroached segments.
    encloop = badfacetraverse(badsubsegs);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// perturbrepairencsubs()    Repair all encroached subfaces.                 //
//                                                                           //
// All encroached subfaces are stored in 'badsubfaces'. Each subface will be //
// split by adding a perturbed point near its circumcenter. However, if the  //
// point encroaches some segments, it will not be inserted.  Instead, the    //
// encroached segments are split.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::perturbrepairencsubs(list* cavsublist, queue* flipqueue)
{
  badface *encloop, *encsubseg;
  tetrahedron encodedtet;
  triface splittet;
  face splitsub, symsplitsub;
  face checkseg, symsplitseg;
  point newpoint, sympoint;
  point pa, pb, pc, pd;
  enum insertsiteresult success;
  enum locateresult loc, symloc;
  REAL cent[3], d1, ps, rs;
  bool reject;
  int i;

  // Note that steinerleft == -1 if an unlimited number of Steiner points
  //   is allowed.  Loop until the list 'badsubfaces' is empty.
  while ((badsubfaces->items > 0) && (steinerleft != 0)) {
    badsubfaces->traversalinit();
    encloop = badfacetraverse(badsubfaces);
    while ((encloop != (badface *) NULL) && (steinerleft != 0)) {
      splitsub = encloop->ss;
#ifdef SELF_CHECK
      assert(shell2badface(splitsub) == encloop);
#endif
      setshell2badface(splitsub, NULL);
      pa = sorg(splitsub);
      pb = sdest(splitsub);
      pc = sapex(splitsub);
      // The subface may be not the same one when it was determined to be
      //   encroached.  If its adjacent encroached subface was split, the
      //   consequent flips may change it into another subface.
      if ((pa == encloop->forg) && (pb == encloop->fdest) &&
          (pc == encloop->fapex)) {
        if (b->verbose > 1) {
          printf("  Get subface (%d, %d, %d).\n", pointmark(pa),
                 pointmark(pb), pointmark(pc));
        }
        // Create the newpoint.
        makepoint(&newpoint);
        // Get the circumcenter of abc.
        circumsphere(pa, pb, pc, NULL, cent, &d1);
#ifdef SELF_CHECK
        assert(d1 > 0.0);
#endif
        // Add a random perturbation to newpoint along the vector a->cent.
        //   This way, the perturbed point still lies in the plane of abc.
        ps = randgenerator(d1 * 1.0e-3);
        rs = ps / d1;
        // Set newpoint (be at the perturbed circumcenter of abc).
        for (i = 0; i < 3; i++) newpoint[i] = cent[i] + rs * (cent[i] - pa[i]);
        // Get the abovepoint of the facet.
        abovepoint = facetabovepointarray[shellmark(splitsub)];
        // Do we need to calculate the abovepoint?
        if (abovepoint == (point) NULL) {
          getfacetabovepoint(&splitsub);
        }
        loc = locatesub(newpoint, &splitsub, 1, 0.0);
#ifdef SELF_CHECK
        assert(loc != ONVERTEX);
#endif
        if (loc != OUTSIDE) {
          // Add 'splitsub' into 'cavsublist'.
          cavsublist->append(&splitsub);
          // Collect all subfaces that encroached by newpoint.
          collectcavsubs(newpoint, cavsublist);
          // Find if there are encroached segments.
          reject = tallencsegsfsubs(newpoint, cavsublist);
          // Clear cavsublist for the next use.
          cavsublist->clear();
        } else {
          // newpoint lies outside. splitsub contains the boundary segment.
          sspivot(splitsub, checkseg);
#ifdef SELF_CHECK
          assert(checkseg.sh != dummysh);
#endif
          // Add this segment into list for splitting.
          if (b->verbose > 2) {
            printf("    Queuing boundary segment (%d, %d).\n",
                   pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
          }
          encsubseg = (badface *) badsubsegs->alloc();
          encsubseg->ss = checkseg;
          encsubseg->forg = sorg(checkseg);
          encsubseg->fdest = sdest(checkseg);
          encsubseg->foppo = (point) NULL;
          setshell2badface(encsubseg->ss, encsubseg);
          // Reject newpoint.
          reject = true;
        }

        if (!reject) {
          // newpoint is going to be inserted.
          
          // Is there periodic boundary condition?
          if (checkpbcs) {
            if (shellpbcgroup(splitsub) >= 0) {
              // Insert a point on another facet of the pbcgroup.
              makepoint(&sympoint);
              // Note: 'abovepoint' will be changed.
              symloc = getsubpbcsympoint(newpoint, &splitsub, sympoint,
                                         &symsplitsub);
#ifdef SELF_CHECK
              assert(symloc != ONVERTEX);
#endif
              setpoint2pbcpt(newpoint, sympoint);
              setpoint2pbcpt(sympoint, newpoint);
              setpointtype(sympoint, FREESUBVERTEX);
              // setpoint2sh(sympoint, sencode(symsplitsub));
              // Insert sympoint into DT.
              pd = sorg(symsplitsub);
              splittet.tet = dummytet;
              // Find a good start point to search.
              encodedtet = point2tet(pd);
              if (encodedtet != (tetrahedron) NULL) {
                decode(encodedtet, splittet);
                if (isdead(&splittet)) {
                  splittet.tet = dummytet; 
                }
              }
              // Locate sympoint in DT.  Do exact location.
              success = insertsite(sympoint, &splittet, false, flipqueue);
#ifdef SELF_CHECK
              assert(success != DUPLICATEPOINT);
#endif
              if (success == OUTSIDEPOINT) {
                inserthullsite(sympoint, &splittet, flipqueue);
              }
              if (steinerleft > 0) steinerleft--;
              // Let sympoint remember splittet.
              setpoint2tet(sympoint, encode(splittet));
              // Do flip in DT.
              flip(flipqueue, NULL);
              // Insert sympoint into F.
              // getabovepoint(&symsplitsub);
              // symloc = locatesub(sympoint, &symsplitsub, 1, 0.0);
              if (symloc == ONFACE) {
                splitsubface(sympoint, &symsplitsub, flipqueue);
              } else if (symloc == ONEDGE) {
                splitsubedge(sympoint, &symsplitsub, flipqueue);
              } else {
                // 'insertsite()' has done the whole job.
#ifdef SELF_CHECK
                assert(symloc == ONVERTEX);
                assert(checksubfaces);
#endif
                // Split subfaces have been flipped.
                flipqueue->clear();
              }
              // Do flip in facet.
              flipsub(flipqueue);
            }
          }

          // Insert newpoint into DT.
          splittet.tet = dummytet;
          // Find a good start point to search.
          encodedtet = point2tet(pa);
          if (encodedtet != (tetrahedron) NULL) {
            decode(encodedtet, splittet);
            if (isdead(&splittet)) {
              splittet.tet = dummytet; 
            }
          }
          if (splittet.tet == dummytet) { // Try pb.
            encodedtet = point2tet(pb);
            if (encodedtet != (tetrahedron) NULL) {
              decode(encodedtet, splittet);
              if (isdead(&splittet)) {
                splittet.tet = dummytet;
              }
            }
          }
          // Locate the newpoint in DT.  Do exact location.
          success = insertsite(newpoint, &splittet, false, flipqueue);
#ifdef SELF_CHECK
          assert(success != DUPLICATEPOINT);
#endif
          if (success == OUTSIDEPOINT) {
            inserthullsite(newpoint, &splittet, flipqueue);
          }
          if (steinerleft > 0) steinerleft--;
          // Let newpoint remember splittet.
          setpoint2tet(newpoint, encode(splittet));
          // Do flip in DT.
          flip(flipqueue, NULL);
          // Insert newpoint into F.
          // if (checkpbcs) {
            // 'abovepoint' has been changed.
            // getabovepoint(&splitsub);
            // loc = locatesub(newpoint, &splitsub, 1, 0.0);
          // }
          if (loc == ONFACE) {
            // Insert the newpoint in facet.
            splitsubface(newpoint, &splitsub, flipqueue);
          } else if (loc == ONEDGE) {
            // Insert the newpoint in facet.
            splitsubedge(newpoint, &splitsub, flipqueue);
          } else {
            // 'insertsite()' has done the whole job.
#ifdef SELF_CHECK
            assert(loc == ONVERTEX);
            assert(checksubfaces);
#endif
            // Split subfaces have been flipped.
            flipqueue->clear();
          }
          // Set the type of the newpoint.
          setpointtype(newpoint, FREESUBVERTEX);
          // Set splitsub into the newpoint.
          // setpoint2sh(newpoint, sencode(splitsub));
          // Do flip in the facet.
          flipsub(flipqueue);

          // Remove this entry from list.
          badfacedealloc(badsubfaces, encloop);
        } else {
          // newpoint is rejected. Remove it from points.
          pointdealloc(newpoint);
          // Repair all encroached segments.
          perturbrepairencsegs(flipqueue);
          // Do not remove 'encloop'. Later it will be tested again.
          setshell2badface(encloop->ss, encloop);
        }
      } else {
        // This subface has been changed. Remove this entry from list.
        badfacedealloc(badsubfaces, encloop);
        // It may be co-circular with its neighbors.
        // checksub4cocir(&splitsub, eps, false, true); 
      }
      // Get the next encroached subfaces.
      encloop = badfacetraverse(badsubfaces);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrperturbvertices()    Remove the local degeneracies in DT.             //
//                                                                           //
// A local degeneracy of a DT D is a set of 5 or more vertices which share a //
// common sphere S and no other vertex of D in S.  D is not unique if it has //
// local degeneracies. This routine removes the local degeneracies from D by //
// inserting break points (as described in reference [2]).                   //
//                                                                           //
// 'eps' is a user-provided error tolerance. It is used to detect whether or //
// not five points are approximate cospherical (evaluated in iscospheric()). //
// Set it to 0.0 to disable it, i.e., only test pure degenerate point set.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::incrperturbvertices(REAL eps)
{
  queue *flipqueue;
  list *cavsublist;
  long vertcount;

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

  vertcount = points->items;
  // Create a map from points to tets for fastening search.
  // makepoint2tetmap();  // This has been done in meshsurface().

  // Initialize working queues, lists.
  flipqueue = new queue(sizeof(badface));
  cavsublist = new list(sizeof(face), NULL, 256);
  // Initialize the pool of encroached subfaces and subsegments.
  badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  // Find all pairs of co-circular subfaces.
  tallcocirsubs(eps, true);
  if (b->verbose && badsubfaces->items > 0) {
    printf("  Removing degenerate subfaces.\n");
  }
  perturbrepairencsubs(cavsublist, flipqueue);

  if (b->verbose > 0) {
    printf("  %ld break points.\n", points->items - vertcount);
  }
  
  delete cavsublist;
  delete flipqueue;
  delete badsubfaces;
  delete badsubsegs; 
  badsubsegs = (memorypool *) NULL;
  badsubfaces = (memorypool *) NULL;
}

//
// End of vertex perturbation routines
//

//
// Begin of segment recovery routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// markacutevertices()    Mark acute vertices.                               //
//                                                                           //
// A vertex v is called acute if there are two segments sharing at v forming //
// an acute angle (i.e. smaller than 90 degree).                             //
//                                                                           //
// This routine finds all acute vertices in the PLC and marks them as point- //
// type ACUTEVERTEX. The other vertices of segments which are non-acute will //
// be marked as NACUTEVERTEX.  Vertices which are not endpoints of segments  //
// (such as DUPLICATEDVERTEX, UNUSEDVERTEX, etc) are not infected.           //
//                                                                           //
// NOTE: This routine should be called before Steiner points are introduced. //
// That is, no point has type like FREESEGVERTEX, etc.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::markacutevertices(REAL acuteangle)
{
  shellface **segsperverlist;
  face segloop, nextseg;
  point pointloop, edest, eapex;
  REAL cosbound, anglearc;
  REAL v1[3], v2[3], L, D;
  bool isacute;
  int *idx2seglist;
  int acutecount;
  int idx, i, j, k;

  if (b->verbose > 0) {
    printf("  Marking acute vertices.\n");
  }

  anglearc = acuteangle * PI / 180.0;
  cosbound = cos(anglearc);
  acutecount = 0;
  // Constructing a map from vertex to segments.
  makesegmentmap(idx2seglist, segsperverlist);

  // Loop over the set of vertices.
  points->traversalinit();
  pointloop = pointtraverse();
  while (pointloop != (point) NULL) {
    idx = pointmark(pointloop) - in->firstnumber;
    // Only do test if p is an endpoint of some segments.
    if (idx2seglist[idx + 1] > idx2seglist[idx]) {
      // Init p to be non-acute.
      setpointtype(pointloop, NACUTEVERTEX);
      isacute = false;
      // Loop through all segments sharing at p.
      for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isacute; i++) {
        segloop.sh = segsperverlist[i];
        // segloop.shver = 0;
        if (sorg(segloop) != pointloop) sesymself(segloop);
        edest = sdest(segloop);
        for (j = i + 1; j < idx2seglist[idx + 1] && !isacute; j++) {
          nextseg.sh = segsperverlist[j];
          // nextseg.shver = 0;
          if (sorg(nextseg) != pointloop) sesymself(nextseg);
          eapex = sdest(nextseg);
          // Check the angle formed by segs (p, edest) and (p, eapex).
          for (k = 0; k < 3; k++) {
            v1[k] = edest[k] - pointloop[k];
            v2[k] = eapex[k] - pointloop[k];
          }
          L = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
          for (k = 0; k < 3; k++) v1[k] /= L;
          L = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);
          for (k = 0; k < 3; k++) v2[k] /= L;
          D = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
          // Is D acute?
          isacute = (D >= cosbound);
        }
      }
      if (isacute) {
        // Mark p to be acute.
        setpointtype(pointloop, ACUTEVERTEX);
        acutecount++;
      }
    }
    pointloop = pointtraverse();
  }

  delete [] idx2seglist;
  delete [] segsperverlist;

  if ((b->verbose > 0) && (acutecount > 0)) {
    printf("  %d acute vertices.\n", acutecount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirection()    Find the first tetrahedron on the path from one point  //
//                    to another.                                            //
//                                                                           //
// Find the tetrahedron that intersects a line segment L (from the origin of //
// 'searchtet' to the point 'tend'), and returns the result in 'searchtet'.  //
// The origin of 'searchtet' does not change, even though the tetrahedron    //
// returned may differ from the one passed in.  This routine is used to find //
// the direction to move in to get from one point to another.                //
//                                                                           //
// The return value notes the location of the line segment L with respect to //
// 'searchtet':                                                              //
//   - Returns RIGHTCOLLINEAR indicates L is collinear with the line segment //
//     from the origin to the destination of 'searchtet'.                    //
//   - Returns LEFTCOLLINEAR indicates L is collinear with the line segment  //
//     from the origin to the apex of 'searchtet'.                           //
//   - Returns TOPCOLLINEAR indicates L is collinear with the line segment   //
//     from the origin to the opposite of 'searchtet'.                       //
//   - Returns ACROSSEDGE indicates L intersects with the line segment from  //
//     the destination to the apex of 'searchtet'.                           //
//   - Returns ACROSSFACE indicates L intersects with the face opposite to   //
//     the origin of 'searchtet'.                                            //
//   - Returns BELOWHULL indicates L crosses outside the mesh domain. This   //
//     can only happen when the domain is non-convex.                        //
//                                                                           //
// NOTE: This routine only works correctly when the mesh is exactly Delaunay.//
//                                                                           //
// If 'maxtetnumber' > 0, stop the searching process if the number of passed //
// tets is larger than it. Return BELOWHULL.                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::finddirectionresult tetgenmesh::
finddirection(triface *searchtet, point tend, long maxtetnumber)
{
  triface neightet;
  point tstart, tdest, tapex, toppo;
  REAL ori1, ori2, ori3;
  long tetnumber;

  tstart = org(*searchtet);
#ifdef SELF_CHECK
  assert(tstart != tend);
#endif
  adjustedgering(*searchtet, CCW);
  if (tstart != org(*searchtet)) {
    enextself(*searchtet); // For keeping the same origin.
  }
  tdest = dest(*searchtet);
  if (tdest == tend) {
    return RIGHTCOLLINEAR;
  }
  tapex = apex(*searchtet); 
  if (tapex == tend) {
    return LEFTCOLLINEAR;
  } 

  ori1 = orient3d(tstart, tdest, tapex, tend);
  if (ori1 > 0.0) {
    // 'tend' is below the face, get the neighbor of this side.
    sym(*searchtet, neightet);
    if (neightet.tet != dummytet) {
      findorg(&neightet, tstart); 
      adjustedgering(neightet, CCW);
      if (org(neightet) != tstart) {
        enextself(neightet); // keep the same origin.
      }
      // Set the changed configuratiuon.
      *searchtet = neightet; 
      ori1 = -1.0; 
      tdest = dest(*searchtet);
      tapex = apex(*searchtet);
    } else {
      // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
      assert(nonconvex);
#endif
      return BELOWHULL; 
    }
  }

  // Repeatedly change the 'searchtet', remain 'tstart' be its origin, until
  //   find a tetrahedron contains 'tend' or is crossed by the line segment
  //   from 'tstart' to 'tend'.
  tetnumber = 0l;
  while ((maxtetnumber > 0) && (tetnumber <= maxtetnumber)) {
    tetnumber++;
    toppo = oppo(*searchtet);
    if (toppo == tend) {
      return TOPCOLLINEAR;
    }
    ori2 = orient3d(tstart, toppo, tdest, tend);
    if (ori2 > 0.0) {
      // 'tend' is below the face, get the neighbor at this side.
      fnext(*searchtet, neightet);
      symself(neightet);
      if (neightet.tet != dummytet) {
        findorg(&neightet, tstart); 
        adjustedgering(neightet, CCW);
        if (org(neightet) != tstart) {
          enextself(neightet); // keep the same origin.
        }
        // Set the changed configuration.
        *searchtet = neightet; 
        ori1 = -1.0; 
        tdest = dest(*searchtet);
        tapex = apex(*searchtet);
        // Continue the search from the changed 'searchtet'.
        continue;
      } else {
        // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
        assert(nonconvex);
#endif
        return BELOWHULL; 
      }
    }
    ori3 = orient3d(tapex, toppo, tstart, tend);
    if (ori3 > 0.0) {
      // 'tend' is below the face, get the neighbor at this side.
      enext2fnext(*searchtet, neightet);
      symself(neightet);
      if (neightet.tet != dummytet) {
        findorg(&neightet, tstart); 
        adjustedgering(neightet, CCW);
        if (org(neightet) != tstart) {
          enextself(neightet); // keep the same origin.
        }
        // Set the changed configuration.
        *searchtet = neightet; 
        ori1 = -1.0; 
        tdest = dest(*searchtet);
        tapex = apex(*searchtet);
        // Continue the search from the changed 'searchtet'.
        continue;
      } else {
        // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
        assert(nonconvex);
#endif
        return BELOWHULL; 
      }
    }
    // Now 'ori1', 'ori2' and 'ori3' are possible be 0.0 or all < 0.0;
    if (ori1 < 0.0) {
      // Possible cases are: ACROSSFACE, ACROSSEDGE, TOPCOLLINEAR.
      if (ori2 < 0.0) {
        if (ori3 < 0.0) {
          return ACROSSFACE;
        } else { // ori3 == 0.0;
          // Cross edge (apex, oppo)
          enext2fnextself(*searchtet);
          esymself(*searchtet); // org(*searchtet) == tstart;
          return ACROSSEDGE;
        }
      } else { // ori2 == 0.0; 
        if (ori3 < 0.0) {
          // Cross edge (dest, oppo)
          fnextself(*searchtet);
          esymself(*searchtet);
          enextself(*searchtet); // org(*searchtet) == tstart;
          return ACROSSEDGE;
        } else { // ori3 == 0.0;
          // Collinear with edge (org, oppo)
          return TOPCOLLINEAR;
        }
      }
    } else { // ori1 == 0.0;
      // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE.
      if (ori2 < 0.0) {
        if (ori3 < 0.0) {
          // Cross edge (tdest, tapex)
          return ACROSSEDGE;
        } else { // ori3 == 0.0
          // Collinear with edge (torg, tapex)
          return LEFTCOLLINEAR;
        }
      } else { // ori2 == 0.0;
#ifdef SELF_CHECK
        assert(ori3 != 0.0);
#endif
        // Collinear with edge (torg, tdest)
        return RIGHTCOLLINEAR;
      }
    }
  }
  // Loop breakout. It may happen when the mesh is non-Delaunay.
  return BELOWHULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsearchtet()    Find a tetrahedron whose origin is either 'p1' or 'p2'. //
//                                                                           //
// On return, the origin of 'searchtet' is either 'p1' or 'p2',  and 'tend'  //
// returns the other point.  'searchtet' serves as the starting tetrahedron  //
// for searching of the line segment from 'p1' to 'p2' or vice versa.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsearchtet(point p1, point p2, triface* searchtet,
  point* tend)
{
  tetrahedron encodedtet1, encodedtet2;

  // Is there a valid handle provided by the user?
  if ((searchtet->tet != (tetrahedron *) NULL) && !isdead(searchtet)) {
    // Find which endpoint the handle holds.
    if (findorg(searchtet, p1)) {
      *tend = p2;
      return;
    } else {
      if (findorg(searchtet, p2)) {
        *tend = p1;
        return;
      }
    }
  }
  // If not, search the tet handle stored in 'p1' or 'p2'.
  *tend = (point) NULL;
  encodedtet1 = point2tet(p1);
  encodedtet2 = point2tet(p2);
  if (encodedtet1 != (tetrahedron) NULL) {
    decode(encodedtet1, *searchtet);
    // Be careful, here 'searchtet' may be dead.
    if (findorg(searchtet, p1)) {
      *tend = p2;
    }
  } else if (encodedtet2 != (tetrahedron) NULL) {
    decode(encodedtet2, *searchtet);
    // Be careful, here 'searchtet' may be dead.
    if (findorg(searchtet, p2)) {
      *tend = p1;
    }
  }
  // If still not, perform a full point location.  The starting tet is
  //   chosen as follows: Use the handle stored in 'p1' or 'p2' if it is
  //   alive; otherwise, start from a tet on the convex hull.
  if (*tend == (point) NULL) {
    if (encodedtet1 != (tetrahedron) NULL) {
      decode(encodedtet1, *searchtet);
      // Be careful, here 'searchtet' may be dead.
    }
    if (isdead(searchtet)) {
      if (encodedtet2 != (tetrahedron) NULL) {
        decode(encodedtet2, *searchtet);
        // Be careful, here 'searchtet' may be dead.
      }
      if (isdead(searchtet)) {
        searchtet->tet = dummytet;
        searchtet->loc = 0;
        symself(*searchtet);
      }
#ifdef SELF_CHECK
      assert(!isdead(searchtet));
#endif
    }
    if (locate(p1, searchtet) != ONVERTEX) {
      printf("Internal error in getsearchtet():  Failed to locate point\n");
      internalerror();
    }
    // Remember this handle in 'p1' to enhance the search speed.
    setpoint2tet(p1, encode(*searchtet));
    *tend = p2;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// isedgeencroached()    Check whether or not a subsegment is encroached.    //
//                                                                           //
// A segment with endpoints 'p1' and 'p2' is encroached by the point 'testpt'//
// if it lies in the diametral sphere of this segment.  The degenerate case  //
// that 'testpt' lies on the sphere is treated as encroached if 'degflag' is //
// set to be TRUE.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::isedgeencroached(point p1, point p2, point testpt,
  bool degflag)
{
  REAL dotproduct;

  // Check if the segment is facing an angle larger than 90 degree?
  dotproduct = (p1[0] - testpt[0]) * (p2[0] - testpt[0])
             + (p1[1] - testpt[1]) * (p2[1] - testpt[1])
             + (p1[2] - testpt[2]) * (p2[2] - testpt[2]);
  if (dotproduct < 0) {
    return true;
  } else if (dotproduct == 0 && degflag) {
    return true;
  } else {
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutrefpoint()    Search the reference point of a missing segment.       //
//                                                                           //
// A segment S is missing in current Delaunay tetrahedralization DT and will //
// be split by inserting a point V in it.  The two end points of S are the   //
// origin of 'searchtet' and 'tend'. And we know that S is crossing the face //
// of 'searchtet' opposite to its origin (may be intersecting with the edge  //
// from the destination to the apex of the 'searchtet').  The search of P is //
// completed by walking through all faces of DT across by S.                 //
//                                                                           //
// Warning:  This routine is correct when the tetrahedralization is Delaunay //
// and convex. Otherwise, the search loop may not terminate.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::scoutrefpoint(triface* searchtet, point tend)
{
  triface checkface;
  point tstart, testpt, refpoint;
  REAL cent[3], radius, largest;
  REAL ahead;
  bool ncollinear;
  int sides;

  if (b->verbose > 2) {
    printf("  Scout the reference point of segment (%d, %d).\n",
           pointmark(org(*searchtet)), pointmark(tend));
  }

  tstart = org(*searchtet);
  refpoint = (point) NULL;
  largest = 0; // avoid compile warning.
  
  // Check the three vertices of the crossing face.
  testpt = apex(*searchtet);
  if (isedgeencroached(tstart, tend, testpt, true)) {
    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
#ifdef SELF_CHECK
    assert(ncollinear);
#endif
    refpoint = testpt;
    largest = radius;
  }
  testpt = dest(*searchtet);
  if (isedgeencroached(tstart, tend, testpt, true)) {
    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
#ifdef SELF_CHECK
    assert(ncollinear);
#endif
    if (refpoint == (point) NULL) {
      refpoint = testpt;
      largest = radius;
    } else {
      if (radius > largest) {
        refpoint = testpt;
        largest = radius;
      }
    }
  }
  testpt = oppo(*searchtet);
  if (isedgeencroached(tstart, tend, testpt, true)) {
    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
#ifdef SELF_CHECK
    assert(ncollinear);
#endif
    if (refpoint == (point) NULL) {
      refpoint = testpt;
      largest = radius;
    } else {
      if (radius > largest) {
        refpoint = testpt;
        largest = radius;
      }
    }
  }
  // Check the opposite vertex of the neighboring tet in case the segment
  //   crosses the edge (leftpoint, rightpoint) of the crossing face.
  sym(*searchtet, checkface);
  if (checkface.tet != dummytet) {
    testpt = oppo(checkface);
    if (isedgeencroached(tstart, tend, testpt, true)) {
      ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
#ifdef SELF_CHECK
      assert(ncollinear);
#endif
      if (refpoint == (point) NULL) {
        refpoint = testpt;
        largest = radius;
      } else {
        if (radius > largest) {
          refpoint = testpt;
          largest = radius;
        }
      }
    }
  }

  // Walk through all crossing faces.
  enextfnext(*searchtet, checkface);
  sym(checkface, *searchtet);
  while (true) {
    // Check if we are reaching the boundary of the triangulation.
#ifdef SELF_CHECK
    assert(searchtet->tet != dummytet);
#endif
    // Search for an adjoining tetrahedron we can walk through.
    searchtet->ver = 0;
    // 'testpt' is the shared vertex for the following orientation tests.
    testpt = oppo(*searchtet);
    if (testpt == tend) {
      // The searching is finished.
      break; 
    } else {
      // 'testpt' may encroach the segment.
      if ((testpt != tstart) && (testpt != refpoint)) {
        if (isedgeencroached(tstart, tend, testpt, true)) {
          ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
          if (!ncollinear) {
            // 'testpt' is collinear with the segment. It may happen when a
            //   set of collinear and continuous segments is defined by two
            //   extreme endpoints.  In this case, we should choose 'testpt'
            //   as the splitting point immediately.  No new point should be
            //   created.
            refpoint = testpt;
            break;
          }
          if (refpoint == (point) NULL) {
            refpoint = testpt;
            largest = radius;
          } else {
            if (radius > largest) {
              refpoint = testpt;
              largest = radius;
            }
          }
        }
      }
    }
    // Check three side-faces of 'searchtet' to find the one through
    //   which we can walk next.
    for (sides = 0; sides < 3; sides++) {
      fnext(*searchtet, checkface);
      ahead = orient3d(org(checkface), dest(checkface), testpt, tend);
      if (ahead < 0.0) {
        // We can walk through this face and continue the searching. 
        sym(checkface, *searchtet);
        break;
      }
      enextself(*searchtet);
    }
#ifdef SELF_CHECK
    assert (sides < 3);
#endif
  }

#ifdef SELF_CHECK
  assert(refpoint != (point) NULL);
#endif
  return refpoint;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsegmentorigin()    Return the origin of the (unsplit) segment.         //
//                                                                           //
// After a segment (or a subsegment) is split. Two resulting subsegments are //
// connecting each other through the pointers saved in their data fields.    //
// With these pointers, the whole (unsplit) segment can be found. 'splitseg' //
// may be a split subsegment.  Returns the origin of the unsplit segment.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsegmentorigin(face* splitseg)
{
  face workseg;
  point farorg;

  farorg = sorg(*splitseg);
  if ((pointtype(farorg) != ACUTEVERTEX) &&
      (pointtype(farorg) != NACUTEVERTEX)) {
    workseg = *splitseg;
    do {
      senext2self(workseg);
      spivotself(workseg);
      if (workseg.sh != dummysh) {
        workseg.shver = 0;  // It's a subsegment.
        if (sdest(workseg) != farorg) {
          sesymself(workseg);
#ifdef SELF_CHECK
          assert(sdest(workseg) == farorg);
#endif
        }
        farorg = sorg(workseg);
        if ((pointtype(farorg) == ACUTEVERTEX) ||
            (pointtype(farorg) == NACUTEVERTEX)) break;
      }
    } while (workseg.sh != dummysh);
  }
#ifdef SELF_CHECK
  assert((pointtype(farorg) == ACUTEVERTEX) ||
         (pointtype(farorg) == NACUTEVERTEX));
#endif
  return farorg;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsplitpoint()    Get a point for splitting a segment.                   //
//                                                                           //
// 'splitseg' is the segment will be split. 'refpoint' is a reference point  //
// for splitting this segment. Moreover, it should not collinear with the    //
// splitting segment. (The collinear case will be detected by iscollinear()  //
// before entering this routine.)  The calculation of the splitting point is //
// governed by three rules introduced in my paper.                           //
//                                                                           //
// After the position is calculated, a new point is created at this location.//
// The new point has one of the two pointtypes: FREESEGVERTEX indicating it  //
// is an inserting vertex on segment, and NACUTEVERTEX indicating it is an   //
// endpoint of a segment which original has type-3 now becomes type-2.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsplitpoint(face* splitseg, point refpoint)
{
  point splitpoint;
  point farorg, fardest;
  point ei, ej, ek, c;
  REAL v[3], r, split;
  REAL d1, d2, ps, rs;
  bool acuteorg, acutedest;
  int stype, rule;
  int i;   

  // First determine the type of the segment (type-1, type-2, or type-3).
  farorg = getsegmentorigin(splitseg);
  acuteorg = (pointtype(farorg) == ACUTEVERTEX);
  sesymself(*splitseg);
  fardest = getsegmentorigin(splitseg);
  acutedest = (pointtype(fardest) == ACUTEVERTEX);
  sesymself(*splitseg);

  ek = (point) NULL; // avoid a compilation warning.

  if (acuteorg) {
    if (acutedest) {
      stype = 3;
    } else {
      stype = 2;
      ek = farorg;
    }
  } else {
    if (acutedest) {
      stype = 2;
      // Adjust splitseg, so that its origin is acute.
      sesymself(*splitseg);
      ek = fardest;
    } else {
      stype = 1;
    }
  }
  ei = sorg(*splitseg);
  ej = sdest(*splitseg);

  if (b->verbose > 1) {
    printf("  Splitting segment (%d, %d) type-%d with refpoint %d.\n",
           pointmark(ei), pointmark(ej), stype, pointmark(refpoint));
  }

  if (stype == 1 || stype == 3) {
    // Use rule-1.
    REAL eij, eip, ejp;
    eij = distance(ei, ej);
    eip = distance(ei, refpoint);
    ejp = distance(ej, refpoint);
    if ((eip < ejp) && (eip < 0.5 * eij)) {
      c = ei;
      r = eip;
    } else if ((eip > ejp) && (ejp < 0.5 * eij)) {
      c = ej;
      ej = ei;
      r = ejp;
    } else {
      c = ei;
      r = 0.5 * eij;
    }
    split = r / eij;
    for (i = 0; i < 3; i++) {
      v[i] = c[i] + split * (ej[i] - c[i]);
    }
    rule = 1;
  } else {
    // Use rule-2 or rule-3.
    REAL eki, ekj, ekp, evj, evp, eiv;
    c = ek;
    eki = distance(ek, ei);  // eki may equal zero.
    ekj = distance(ek, ej);
    ekp = distance(ek, refpoint);
    // Calculate v (the going to split position between ei, ej).
    r = ekp;
    // Check the validity of the position.
    if (!(eki < r && r < ekj)) {
      printf("Error:  Invalid PLC.\n");
      printf("  Hint:  Use -d switch to check it.\n");
      terminatetetgen(1);
    }
    split = r / ekj;
    for (i = 0; i < 3; i++) {
      v[i] = c[i] + split * (ej[i] - c[i]);
    }
    rule = 2;
    evj = ekj - r; // distance(v, ej);
    evp = distance(v, refpoint);
    if (evj < evp) {
      // v is rejected, use rule-3.
      eiv = distance(ei, v);
      if (evp <= 0.5 * eiv) {
        r = eki + eiv - evp;
      } else {
        r = eki + 0.5 * eiv;
      }
#ifdef SELF_CHECK
      assert(eki < r && r < ekj);
#endif
      split = r / ekj;
      for (i = 0; i < 3; i++) {
        v[i] = c[i] + split * (ej[i] - c[i]);
      }
      if (b->verbose > 1) {
        printf("    Using rule-3.\n");
      }
      rule = 3;
    }
  }

  // Accumulate the corresponding counters.
  if (rule == 1) r1count++;
  else if (rule == 2) r2count++;
  else if (rule == 3) r3count++;

  if (b->verbose > 1) {
    if (stype == 2) {
      printf("    Split = %.12g.\n", distance(ei, v) / distance(ei, ej));
    } else {
      printf("    Split = %.12g.\n", distance(c, v) / distance(c, ej));
    }
  }

  // Create the newpoint.
  makepoint(&splitpoint);
  // Add a random perturbation on splitpoint.
  d1 = distance(c, v);
  d2 = distance(refpoint, v);
  if (stype == 1 || stype == 3) {
    ps = randgenerator(d1 * 1.0e-3);
  } else {
    // For type-2 segment, add a smaller perturbation.
    // ps = randgenerator(d1 * 1.0e-5);
    // REAL d2 = distance(refpoint, v);
    ps = randgenerator(d2 * 1.0e-5);
  }
  rs = ps / d1;
  // Perturb splitpoint away from c.
  for (i = 0; i < 3; i++) {
    splitpoint[i] = c[i] + (1.0 + rs) * (v[i] - c[i]);
  }
  // for (i = 0; i < in->numberofpointattributes; i++) {
  //   splitpoint[i + 3] = c[i + 3] + (split + rs) * (ej[i + 3] - c[i + 3]);
  // }
  if (stype == 3) {
    // Change a type-3 segment into two type-2 segments. 
    setpointtype(splitpoint, NACUTEVERTEX);
  } else {
    // Set it's type be FREESEGVERTEX.
    setpointtype(splitpoint, FREESEGVERTEX);
  }
  setpoint2sh(splitpoint, sencode(*splitseg));

  return splitpoint;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertsegment()    Insert segment into DT. Queue it if it does not exist. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::insertsegment(face *insseg, list *misseglist)
{
  badface *misseg;
  triface searchtet, spintet;
  point tend, checkpoint;
  point p1, p2;
  enum finddirectionresult collinear;
  int hitbdry;

  // Search segment ab in DT.
  p1 = (point) insseg->sh[3]; 
  p2 = (point) insseg->sh[4];
  getsearchtet(p1, p2, &searchtet, &tend);
  collinear = finddirection(&searchtet, tend, tetrahedrons->items);
  if (collinear == LEFTCOLLINEAR) {
    checkpoint = apex(searchtet);
    enext2self(searchtet);
    esymself(searchtet);
  } else if (collinear == RIGHTCOLLINEAR) {
    checkpoint = dest(searchtet);
  } else if (collinear == TOPCOLLINEAR) {
    checkpoint = oppo(searchtet);
    fnextself(searchtet);
    enext2self(searchtet);
    esymself(searchtet);
  } else {
    // assert(collinear == ACROSSFACE || collinear == ACROSSEDGE);
    checkpoint = (point) NULL;
  }
  if (checkpoint == tend) {
    // Segment exist. Bond it to all tets containing it.
    hitbdry = 0;
    adjustedgering(searchtet, CCW);
    fnextself(searchtet);
    spintet = searchtet;
    do {
      tssbond1(spintet, *insseg);
      if (!fnextself(spintet)) {
        hitbdry++;
        if (hitbdry < 2) {
          esym(searchtet, spintet);
          if (!fnextself(spintet)) {
            hitbdry++;
          }
        }
      }
    } while ((apex(spintet) != apex(searchtet)) && (hitbdry < 2));
    return true;
  } else {
    // Segment is missing.
    if (misseglist != (list *) NULL) {
      if (b->verbose > 2) {
        printf("    Queuing missing segment (%d, %d).\n", pointmark(p1),
               pointmark(p2));
      }
      misseg = (badface *) misseglist->append(NULL);
      misseg->ss = *insseg;
      misseg->forg = p1;
      misseg->fdest = p2;
      misseg->foppo = (point) NULL; // Not used.
      // setshell2badface(misseg->ss, misseg);
    }
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallmissegs()    Find and queue all missing segments in DT.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallmissegs(list *misseglist)
{
  face segloop;

  if (b->verbose) {
    printf("  Queuing missing segments.\n");
  }

  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    insertsegment(&segloop, misseglist);  
    segloop.sh = shellfacetraverse(subsegs);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizesegments()    Split segments repeatedly until they appear in a   //
//                        Delaunay tetrahedralization.                       //
//                                                                           //
// Given a PLC X, which has a set V of vertices and a set of segments. Start //
// from a Delaunay tetrahedralization D of V, this routine recovers segments //
// of X in D by incrementally inserting points on missing segments, updating //
// D with the newly inserted points into D', which remains to be a Delaunay  //
// tetrahedralization and respects the segments of X. Hence, each segment of //
// X appears as a union of edges in D'.                                      //
//                                                                           //
// This routine dynamically maintains two meshes, one is DT, another is the  //
// surface mesh F of X.  DT and F have exactly the same vertices.  They are  //
// updated simultaneously with the newly inserted points.                    //
//                                                                           //
// Missing segments are found by looping the set S of segments, checking the //
// existence of each segment in DT.  Once a segment is found missing in DT,  //
// it is split into two subsegments by inserting a point into both DT and F, //
// and S is updated accordingly.  However, the inserted point may cause some //
// other existing segments be non-Delaunay,  hence are missing from the DT.  //
// In order to force all segments to appear in DT, we have to loop S again   //
// after some segments are split. (A little ugly method)  Use a handle to    //
// remember the last segment be split in one loop, hence all segments after  //
// it are existing and need not be checked.                                  //
//                                                                           //
// In priciple, a segment on the convex hull should exist in DT. However, if //
// there are four coplanar points on the convex hull, and the DT only can    //
// contain one diagonal edge which is unfortunately not the segment, then it //
// is missing. During the recovery of the segment, it is possible that the   //
// calculated inserting point for recovering this convex hull segment is not //
// exact enough and lies (slightly) outside the DT. In order to insert the   //
// point, we enlarge the convex hull of the DT, so it can contain the point  //
// and remains convex.  'inserthullsite()' is called for this case.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizesegments()
{
  list *misseglist;
  queue *flipqueue;
  badface *misloop;
  tetrahedron encodedtet;
  triface searchtet, splittet;
  face splitsh, symsplitsub;
  face segloop, symsplitseg;
  point refpoint, splitpoint, sympoint;
  point tend, checkpoint;
  point p1, p2, pa;
  enum finddirectionresult collinear;
  enum insertsiteresult success;
  enum locateresult symloc;
  bool coll;
  long vertcount;
  int i, j;

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

  // Construct a map from points to tets for speeding point location.
  makepoint2tetmap();
  // Initialize a flipqueue.
  flipqueue = new queue(sizeof(badface));
  // Initialize the pool of missing segments.
  misseglist = new list(sizeof(badface), NULL, SUBPERBLOCK);
  // Looking for missing segments.
  tallmissegs(misseglist);
  // The DT contains segments now.
  checksubsegs = 1;
  // Remember the current number of points.
  vertcount = points->items;
  // Initialize the counters.
  r1count = r2count = r3count = 0l;

  // Loop until 'misseglist' is empty.
  while (misseglist->items > 0) {
    // Randomly pick a missing segment to recover.
    i = randomnation(misseglist->items);
    misloop = (badface *)(* misseglist)[i];
    segloop = misloop->ss;
    // Fill the "hole" in the list by filling the last one.
    *misloop = *(badface *)(* misseglist)[misseglist->items - 1];
    misseglist->items--;
    // Now recover the segment.
      p1 = (point) segloop.sh[3];
      p2 = (point) segloop.sh[4];
      if (b->verbose > 1) {
        printf("  Recover segment (%d, %d).\n", pointmark(p1), pointmark(p2));
      }
      getsearchtet(p1, p2, &searchtet, &tend);
      collinear = finddirection(&searchtet, tend, tetrahedrons->items);
      if (collinear == LEFTCOLLINEAR) {
        checkpoint = apex(searchtet);
      } else if (collinear == RIGHTCOLLINEAR) {
        checkpoint = dest(searchtet);
      } else if (collinear == TOPCOLLINEAR) {
        checkpoint = oppo(searchtet);
      } else {
#ifdef SELF_CHECK
        assert(collinear == ACROSSFACE || collinear == ACROSSEDGE);
#endif
        checkpoint = (point) NULL;
      }
      if (checkpoint != tend) {
        // ab is missing.
        splitpoint = (point) NULL;
        if (checkpoint != (point) NULL) {
          // An existing point c is found on the segment. It can happen when
          //   ab is defined by a long segment with c inside it. Use c to
          //   split ab. No new point is created.
          splitpoint = checkpoint;
          if (pointtype(checkpoint) == FREEVOLVERTEX) {
            // c is not a segment vertex yet. It becomes NACUTEVERTEX.
            setpointtype(splitpoint, NACUTEVERTEX);  
          } else if (pointtype(checkpoint) == ACUTEVERTEX) {
            // c is an acute vertex. The definition of PLC is wrong.
          } else if (pointtype(checkpoint) == NACUTEVERTEX) {
            // c is an nonacute vertex. The definition of PLC is wrong.
          } else {
            // assert(0);
          }
        } else {
          // Find a reference point p of ab.
          refpoint = scoutrefpoint(&searchtet, tend);
          if (pointtype(refpoint) == FREEVOLVERTEX) {
            // p is an input point, check if it is nearly collinear with ab.
            coll = iscollinear(p1, p2, refpoint, b->epsilon);
            if (coll) {
              // a, b, and p are collinear. We insert p into ab. p becomes
              //   a segment vertex with type NACUTEVERTEX.
              splitpoint = refpoint;
              setpointtype(splitpoint, NACUTEVERTEX);
            }
          }
          if (splitpoint == (point) NULL) {
            // Calculate a split point v using rule 1, or 2, or 3.
            splitpoint = getsplitpoint(&segloop, refpoint);
            
            // Is there periodic boundary conditions?
            if (checkpbcs) {
              // Yes! Insert points on other segments of incident pbcgroups.
              i = shellmark(segloop) - 1;
              for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) {
                makepoint(&sympoint);
                symloc = getsegpbcsympoint(splitpoint, &segloop, sympoint,
                                           &symsplitseg, segpglist[j]);
#ifdef SELF_CHECK
                assert(symloc != OUTSIDE);
#endif
                if ((symloc == ONEDGE) && (symsplitseg.sh != segloop.sh)) {
#ifdef SELF_CHECK
                  assert(symsplitseg.sh != dummysh);
#endif
                  setpointtype(sympoint, FREESEGVERTEX);
                  setpoint2sh(sympoint, sencode(symsplitseg));
                  // Insert sympoint into DT.
                  pa = sorg(symsplitseg);
                  splittet.tet = dummytet;
                  // Find a good start point to search.
                  encodedtet = point2tet(pa);
                  if (encodedtet != (tetrahedron) NULL) {
                    decode(encodedtet, splittet);
                    if (isdead(&splittet)) {
                      splittet.tet = dummytet; 
                    }
                  }
                  // Locate sympoint in DT.  Do exact location.
                  success = insertsite(sympoint, &splittet, false, flipqueue);
#ifdef SELF_CHECK
                  assert(success != DUPLICATEPOINT);
#endif
                  if (success == OUTSIDEPOINT) {
                    inserthullsite(sympoint, &splittet, flipqueue);
                  }
                  if (steinerleft > 0) steinerleft--;
                  // Let sympoint remember splittet.
                  setpoint2tet(sympoint, encode(splittet));
                  // Do flip in DT.
                  lawson(misseglist, flipqueue);
                  // Insert sympoint into F.
                  symsplitseg.shver = 0;
                  spivot(symsplitseg, symsplitsub);
                  // sympoint should on the edge of symsplitsub.
                  splitsubedge(sympoint, &symsplitsub, flipqueue);
                  // Do flip in facet.
                  flipsub(flipqueue);
                  // Insert the two subsegments.
                  symsplitseg.shver = 0;
                  insertsegment(&symsplitseg, misseglist);
                  senextself(symsplitseg);
                  spivotself(symsplitseg);
                  symsplitseg.shver = 0;
                  insertsegment(&symsplitseg, misseglist);
                } else { // if (symloc == ONVERTEX) {
                  // The sympoint already exists. It is possible when two
                  //   pbc groups are exactly the same. Omit this point.
                  pointdealloc(sympoint);
                }
              }
            }

            // Insert 'splitpoint' into DT.
            if (isdead(&searchtet)) searchtet.tet = dummytet;
            success = insertsite(splitpoint, &searchtet, false, flipqueue);
            if (success == OUTSIDEPOINT) {
              // A convex hull edge is missing, and the inserting point lies
              //   (slightly) outside the convex hull due to the significant
              //   digits lost in the calculation. Enlarge the convex hull.
              inserthullsite(splitpoint, &searchtet, flipqueue);
            }
            if (steinerleft > 0) steinerleft--;
            // Remember a handle in 'splitpoint' to enhance the speed of
            //   consequent point location.
            setpoint2tet(splitpoint, encode(searchtet));
            // Maintain Delaunayness in DT.
            lawson(misseglist, flipqueue);
          }
        }
        // Insert 'splitpoint' into F.
        spivot(segloop, splitsh);
        splitsubedge(splitpoint, &splitsh, flipqueue);
        flipsub(flipqueue);
        // Insert the two subsegments.
        segloop.shver = 0;
        insertsegment(&segloop, misseglist);
        senextself(segloop);
        spivotself(segloop);
        segloop.shver = 0;
        insertsegment(&segloop, misseglist);
      }
  }

  // Detach all segments from tets.
  tetrahedrons->traversalinit();
  searchtet.tet = tetrahedrontraverse();
  while (searchtet.tet != (tetrahedron *) NULL) {
    for (i = 0; i < 6; i++) {
      searchtet.tet[8 + i] = (tetrahedron) dummysh;
    }
    searchtet.tet = tetrahedrontraverse();
  }
  // No segments now.
  checksubsegs = 0;

  if (b->verbose > 0) {
    printf("  %ld protect points.\n", points->items - vertcount);
    printf("  R1: %ld,  R2: %ld,  R3: %ld.\n", r1count, r2count, r3count);
  }

  delete flipqueue;
  delete misseglist;
}

//
// End of segments recovery routines
//

//
// Begin of facet recovery routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertsubface()    Fix a subface in place.                                //
//                                                                           //
// Search a subface s in current tetrahedralization T.  If s is found a face //
// face of T, it is inserted into T.  Return FALSE if s is not found in T.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::insertsubface(face* insertsh, triface* searchtet)
{
  triface spintet, symtet;
  face testsh, testseg;
  face spinsh, casin, casout;
  point tapex, checkpoint;
  enum finddirectionresult collinear;
  int hitbdry;

  // Search an edge of s.
  getsearchtet(sorg(*insertsh), sdest(*insertsh), searchtet, &checkpoint);
  collinear = finddirection(searchtet, checkpoint, tetrahedrons->items);
  if (collinear == LEFTCOLLINEAR) {
    enext2self(*searchtet);
    esymself(*searchtet);
  } else if (collinear == TOPCOLLINEAR) {
    fnextself(*searchtet);
    enext2self(*searchtet);
    esymself(*searchtet);
  }
  if (dest(*searchtet) != checkpoint) {
    // The edge doesn't exist => s is missing.
    return false;
  }

  // Search s by spinning faces around the edge.
  tapex = sapex(*insertsh);
  spintet = *searchtet;
  hitbdry = 0;
  do {
    if (apex(spintet) == tapex) {
      // Found s in T. Check if s has already been inserted.
      tspivot(spintet, testsh);
      if (testsh.sh == dummysh) {
        adjustedgering(spintet, CCW);
        findedge(insertsh, org(spintet), dest(spintet));
        tsbond(spintet, *insertsh);
        sym(spintet, symtet); // 'symtet' maybe outside, use it anyway.
        sesymself(*insertsh);
        tsbond(symtet, *insertsh);
      } else {
        // Found a duplicated subface (due to the redundant input).
        if (!b->quiet) {
          printf("Warning:  Two subfaces are found duplicated at ");
          printf("(%d, %d, %d)\n", pointmark(sorg(testsh)),
                 pointmark(sdest(testsh)), pointmark(sapex(testsh)));
          printf("  Subface of facet #%d is deleted.\n", shellmark(*insertsh));
          // printf("  Hint: -d switch can find all duplicated facets.\n");
        }
        shellfacedealloc(subfaces, insertsh->sh);
      }
      return true;
    }
    if (!fnextself(spintet)) {
      hitbdry ++;
      if (hitbdry < 2) {
        esym(*searchtet, spintet);
        if (!fnextself(spintet)) {
          hitbdry ++;
        }
      }
    }
  } while (hitbdry < 2 && apex(spintet) != apex(*searchtet));

  // s is missing.
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tritritest()    Test if two triangles are intersecting in their interior. //
//                                                                           //
// One triangle t1 is the face of 'checktet', the other t2 is given by three //
// corners 'p1', 'p2' and 'p3'. This routine calls tri_tri_inter() to detect //
// whether t1 and t2 exactly intersect in their interior. Cases like share a //
// vertex, share an edge, or coincidence are considered not intersect.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tritritest(triface* checktet, point p1, point p2, point p3)
{
  point forg, fdest, fapex;
  enum interresult intersect;

  forg = org(*checktet);
  fdest = dest(*checktet);
  fapex = apex(*checktet);

#ifdef SELF_CHECK
  REAL ax, ay, az, bx, by, bz;
  REAL n[3];
  // face (torg, tdest, tapex) should not be degenerate. However p1, p2,
  //   and p3 may be collinear. Check it.
  ax = forg[0] - fdest[0];
  ay = forg[1] - fdest[1];
  az = forg[2] - fdest[2];
  bx = forg[0] - fapex[0];
  by = forg[1] - fapex[1];
  bz = forg[2] - fapex[2];
  n[0] = ay * bz - by * az;
  n[1] = az * bx - bz * ax;
  n[2] = ax * by - bx * ay;
  assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0);
  // The components of n should not smaller than the machine epsilon.

  ax = p1[0] - p2[0];
  ay = p1[1] - p2[1];
  az = p1[2] - p2[2];
  bx = p1[0] - p3[0];
  by = p1[1] - p3[1];
  bz = p1[2] - p3[2];
  n[0] = ay * bz - by * az;
  n[1] = az * bx - bz * ax;
  n[2] = ax * by - bx * ay;
  assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0);
  // The components of n should not smaller than the machine epsilon.
#endif

  intersect = tri_tri_inter(forg, fdest, fapex, p1, p2, p3);
  return intersect == INTERSECT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initializecavity()    Initialize the cavity.                              //
//                                                                           //
// A cavity C is bounded by a list of faces, called fronts.  Each front f is //
// hold by a tet t adjacent to C, t is not in C (uninfected). If f is a hull //
// face, t does't exist, a fake tet t' is created to hold f. t' has the same //
// vertices as f but no opposite.  t' will be removed automatically after C  //
// is filled with new tets (by carvecavity()).                               //
//                                                                           //
// The faces of C are given in two lists. 'floorlist' is a set of subfaces,  //
// each subface has been oriented to face to the inside of C.  'ceillist' is //
// a set of tetrahedral faces. 'frontlist' returns the initialized fronts.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initializecavity(list* floorlist, list* ceillist,
  list* frontlist)
{
  triface neightet, casingtet;
  triface faketet;
  face worksh;
  int i;

  // Initialize subfaces of C.
  for (i = 0; i < floorlist->len(); i++) {
    // Get a subface s.
    worksh = * (face *)(* floorlist)[i];
#ifdef SELF_CHECK
    // Current side of s should be empty.
    stpivot(worksh, neightet);
    assert(neightet.tet == dummytet);
#endif
    // Get the adjacent tet t.
    sesymself(worksh);
    stpivot(worksh, casingtet);
    // Does t exist?
    if (casingtet.tet == dummytet) {
      // Create a fake tet t' to hold f temporarily.
      maketetrahedron(&faketet);
      setorg(faketet, sorg(worksh));
      setdest(faketet, sdest(worksh));
      setapex(faketet, sapex(worksh));
      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
      tsbond(faketet, worksh);
      frontlist->append(&faketet);
    } else {
      frontlist->append(&casingtet);
    }
  }
  // Initialize tet faces of C.
  for (i = 0; i < ceillist->len(); i++) {
    // Get a tet face c.
    neightet = * (triface *) (* ceillist)[i];
#ifdef SELF_CHECK
    // The tet of c must be inside C (going to be deleted).
    assert(infected(neightet));
#endif
    // Get the adjacent tet t.
    sym(neightet, casingtet);
    // Does t exist?
    if (casingtet.tet == dummytet) {
      // No. Create a fake tet t' to hold f temporarily.
      maketetrahedron(&faketet);
      // Be sure that the vertices of t' are CCW oriented.
      adjustedgering(neightet, CW); // CW edge ring.
      setorg(faketet, org(neightet));
      setdest(faketet, dest(neightet));
      setapex(faketet, apex(neightet));
      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
      // Bond t' to a subface if it exists.
      tspivot(neightet, worksh);
      if (worksh.sh != dummysh) {
        sesymself(worksh);
        tsbond(faketet, worksh);
      } 
      // Bond c <--> t'. So we're able to find t' and remove it.
      bond(faketet, neightet);
      // c may become uninfected due to the bond().
      infect(neightet);
      frontlist->append(&faketet);
    } else {
      frontlist->append(&casingtet);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// retrievenewtets()    Retrieve the newly created tets.                     //
//                                                                           //
// On input, 'newtetlist' contains at least one alive new tet. From this tet,//
// other new tets can be found by a broadth-first searching.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::retrievenewtets(list* newtetlist)
{
  triface searchtet, casingtet;
  int i;

  // There may be dead tets due to flip32(). Delete them first.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    if (isdead(&searchtet)) {
      newtetlist->del(i, 0); i--;
      continue;
    }
    infect(searchtet);
  }
  // Find all new tets.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) {
      sym(searchtet, casingtet);
      if ((casingtet.tet != dummytet) && !infected(casingtet)) {
        infect(casingtet);
        newtetlist->append(&casingtet);
      }
    }
  }
  // Uninfect new tets.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    uninfect(searchtet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizecavvertices()    Form a DT of the vertices of a cavity.          //
//                                                                           //
// 'floorptlist' and 'ceilptlist' are the vertices of the cavity.            //
//                                                                           //
// The tets of the DT are created directly in the pool 'tetrahedrons', i.e., //
// no auxiliary data structure and memory are required.  The trick is at the //
// time they're created, there are no connections between them to the other  //
// tets in the pool. You can imagine they form an ioslated island.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist,
  list* ceilptlist, list* newtetlist, queue* flipque)
{
  point *insertarray;
  triface bakhulltet, newtet;
  long bakhullsize;
  long arraysize;
  int bakchksub;
  int i, j;

  // Prepare the array of points for inserting.
  arraysize = floorptlist->len();
  if (ceilptlist != (list *) NULL) {
    arraysize += ceilptlist->len();
  }
  insertarray = new point[arraysize];
  for (i = 0; i < floorptlist->len(); i++) {
    insertarray[i] = * (point *)(* floorptlist)[i];
  }
  if (ceilptlist != (list *) NULL) {
    for (j = 0; j < ceilptlist->len(); j++) {
      insertarray[i + j] = * (point *)(* ceilptlist)[j];
    }
  }

  // The incrflipdelaunay() is re-used. Backup global variables.
  decode(dummytet[0], bakhulltet);
  bakhullsize = hullsize;
  bakchksub = checksubfaces;
  checksubfaces = 0;
  b->verbose--;

  // Form the DT by incremental flip Delaunay algorithm. Do not jump for
  //   point location, do not merge points.
  incrflipdelaunay(oldtet, insertarray, arraysize, false, false, 0.0, flipque);
  
  // Get a tet in D.
  decode(dummytet[0], newtet);
  newtetlist->append(&newtet);
  // Get all tets of D.
  retrievenewtets(newtetlist);

  // Restore global variables.
  dummytet[0] = encode(bakhulltet);
  hullsize = bakhullsize;
  checksubfaces = bakchksub;
  b->verbose++;
  
  delete [] insertarray;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertauxsubface()    Fix an auxilary subface in place.                   //
//                                                                           //
// An auxilary subface s is fixed in D as it is a real subface, but s has no //
// vertices and neighbors. It has two uses: (1) it protects an identfied     //
// front f in D; (2) it serves the link to bond a tet in C and f later. The  //
// first neighbor of s (s->sh[0]) stores a pointer to f.                     //
//                                                                           //
// 'front' is a front f of C. idfront' t is a tet in D where f is identified //
// be a face of it. s will be fixed between t and its neighbor.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertauxsubface(triface* front, triface* idfront)
{
  triface neightet;
  face auxsh;

  // Create the aux subface s.
  makeshellface(subfaces, &auxsh);
  // Bond s <--> t.
  tsbond(*idfront, auxsh);
  // Does t's neighbor n exist?
  sym(*idfront, neightet);
  if (neightet.tet != dummytet) {
    // Bond s <--> n.
    sesymself(auxsh);
    tsbond(neightet, auxsh);
  }
  // Let s remember f.
  auxsh.sh[0] = (shellface) encode(*front);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutfront()    Scout a face in D.                                        //
//                                                                           //
// Search a 'front' f in D. If f is found, return TRUE and the face of D is  //
// returned in 'idfront'. Otherwise, return FALSE.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::scoutfront(triface* front, triface* idfront, list* newtetlist)
{
  triface spintet;
  point pa, pb, pc;
  enum locateresult loc;
  enum finddirectionresult col;
  int hitbdry;
  int i;

  // Let the front we're searching is abc.
  pa = org(*front);
  pb = dest(*front);
  // Get a tet in D for searching.
  *idfront = recenttet;
  // Make sure the tet is valid (it may be killed by flips).
  if (isdead(idfront)) {
    // The tet is dead. Search a live tet in D. !!!
    for (i = 0; i < newtetlist->len(); i++) {
      recenttet = * (triface *)(* newtetlist)[i];
      if (!isdead(&recenttet)) break;
    }
    assert(i < newtetlist->len());
  }

  // Search a tet having vertex a.
  loc = preciselocate(pa, idfront, (long) newtetlist->len());
  assert(loc == ONVERTEX);
  recenttet = *idfront;
  // Search a tet having edge ab.
  col = finddirection(idfront, pb, (long) newtetlist->len());
  if (col == RIGHTCOLLINEAR) {
    // b is just the destination.
  } else if (col == LEFTCOLLINEAR) {
    enext2self(*idfront);
    esymself(*idfront);
  } else if (col == TOPCOLLINEAR) {
    fnextself(*idfront);
    enext2self(*idfront);
    esymself(*idfront);
  }

  if (dest(*idfront) == pb) {
    // Search a tet having face abc
    pc = apex(*front);
    spintet = *idfront;
    hitbdry = 0;
    do {
      if (apex(spintet) == pc) {
        // Found abc. Insert an auxilary subface s at idfront.
        // insertauxsubface(front, &spintet);
        *idfront = spintet;
        return true;
      }
      if (!fnextself(spintet)) {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(*idfront, spintet);
          if (!fnextself(spintet)) {
            hitbdry ++;
          }
        }
      }
      if (apex(spintet) == apex(*idfront)) break;
    } while (hitbdry < 2);  
  }

  // f is missing in D.
  if (b->verbose > 2) {
    printf("    Front (%d, %d, %d) is missing.\n", pointmark(pa),
           pointmark(pb), pointmark(apex(*front)));
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// gluefronts()    Glue two fronts together.                                 //
//                                                                           //
// This is a support routine for identifyfront().  Two fronts f and f1 are   //
// found indentical. This is caused by the non-coplanarity of vertices of a  //
// facet. Hence f and f1 are a subface and a tet. They are not fronts of the //
// cavity anymore. This routine glues f and f1 together.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::gluefronts(triface* front, triface* front1)
{
  face consh;

  // Glue f and f1 together. There're four cases:
  //   (1) both f and f1 are not fake;
  //   (2) f is not fake, f1 is fake;
  //   (3) f is fake and f1 is not fake;
  //   (4) both f and f1 are fake.
  // Case (4) should be not possible. 

  // Is there a concrete subface c at f.
  tspivot(*front, consh);
  if (consh.sh != dummysh) {
    sesymself(consh);
    tsbond(*front1, consh); // Bond: f1 <--> c.
    sesymself(consh);
  }
  // Does f hold by a fake tet.
  if (oppo(*front) == (point) NULL) {
    // f is fake. Case (3) or (4).
    assert(oppo(*front1) != (point) NULL); // Eliminate (4).
    // Case (3).
    if (consh.sh != dummysh) {
      stdissolve(consh);  // Dissolve: c -x-> f.
    }
    // Dealloc f.
    tetrahedrondealloc(front->tet);
    // f1 becomes a hull. let 'dummytet' bond to it.
    dummytet[0] = encode(*front1);
  } else {
    // Case (1) or (2).
    bond(*front, *front1); // Bond f1 <--> f. 
  }
  // Is f a fake tet?
  if (!isdead(front)) {
    // No. Check for case (2).
    tspivot(*front1, consh);
    // Is f1 fake?
    if (oppo(*front1) == (point) NULL) {
      // Case (2) or (4)
      assert(oppo(*front) != (point) NULL); // Eliminate (4).
      // Case (2).
      if (consh.sh != dummysh) {
        stdissolve(consh);  // Dissolve: c -x-> f1.
        sesymself(consh); // Bond: f <--> c.
        tsbond(*front, consh);
      }
      // Dissolve: f -x->f1.
      dissolve(*front);
      // Dealloc f1.
      tetrahedrondealloc(front1->tet);
      // f becomes a hull. let 'dummytet' bond to it.
      dummytet[0] = encode(*front);
    } else {
      // Case (1).
      if (consh.sh != dummysh) {
        sesymself(consh);
        tsbond(*front, consh); // Bond: f <--> c.
      } 
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// identifyfronts()    Identify cavity faces in D.                           //
//                                                                           //
// 'frontlist' are fronts of C need indentfying.  This routine searches each //
// front f in D. Once f is found, an auxilary subface s is inserted in D at  //
// the face. If f is not found in D, remove it from frontlist and save it in //
// 'misfrontlist'.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist,
  list* newtetlist)
{
  triface front, front1, tfront;
  triface idfront, neightet;
  face auxsh;
  int len, i, j;

  misfrontlist->clear();
  // Set a new tet in D for searching.
  recenttet = * (triface *)(* newtetlist)[0];

  // Identify all fronts in D.
  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)( *frontlist)[i];
    if (scoutfront(&front, &idfront, newtetlist)) {
      // Found f. Insert an aux subface s.
      assert((idfront.tet != dummytet) && !isdead(&idfront));
      // Does s already exist?
      tspivot(idfront, auxsh);
      if (auxsh.sh != dummysh) {
        // There're two identical fronts, f (front) and f1 (s.sh[0])!
        decode((tetrahedron) auxsh.sh[0], front1);
        assert((front1.tet != dummytet) && !infected(front1));
        // Detach s in D.
        tsdissolve(idfront);
        sym(idfront, neightet);
        if (neightet.tet != dummytet) {
          tsdissolve(neightet);
        }
        // s has fulfilled its duty. Can be deleted.
        shellfacedealloc(subfaces, auxsh.sh);
        // Remove f from frontlist.
        frontlist->del(i, 1); i--;
        // Remove f1 from frontlist.
        len = frontlist->len();
        for (j = 0; j < frontlist->len(); j++) {
          tfront = * (triface *)(* frontlist)[j];
          if ((tfront.tet == front1.tet) && (tfront.loc == front1.loc)) {
            // Found f1 in list.  Check f1 != f.
            assert((tfront.tet != front.tet) || (tfront.loc != front.loc));
            frontlist->del(j, 1); i--;
            break;
          }
        }
        assert((frontlist->len() + 1) == len);
        // Glue f and f1 together.
        gluefronts(&front, &front1);        
      } else {
        // Insert an aux subface to protect f in D.
        insertauxsubface(&front, &idfront);
      }
    } else {
      // f is missing.
      frontlist->del(i, 1); i--;
      // Are there two identical fronts, f (front) and f1 (front1)?
      for (j = 0; j < misfrontlist->len(); j++) {
        front1 = * (triface *)(* misfrontlist)[j];
        if (isfacehaspoint(&front1, org(front)) &&
            isfacehaspoint(&front1, dest(front)) &&
            isfacehaspoint(&front1, apex(front))) break;
      }
      if (j < misfrontlist->len()) {
        // Found an identical front f1. Remove f1 from the list.
        misfrontlist->del(j, 1);
        // Glue f and f1 together.
        gluefronts(&front, &front1); 
      } else {
        // Add f into misfrontlist.
        misfrontlist->append(&front);
      }
    }
  }
  return misfrontlist->len() == 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// detachauxsubfaces()    Detach auxilary subfaces in D.                     //
//                                                                           //
// This is a reverse routine of identifyfronts(). Some fronts are missing in //
// D. C can not be easily tetrahedralized. It needs remediation (expansion,  //
// or constrained flips, or adding a Steiner point).  This routine detaches  //
// the auxilary subfaces have been inserted in D and delete them.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::detachauxsubfaces(list* newtetlist)
{
  triface newtet, neightet;
  face auxsh;
  int i;

  for (i = 0; i < newtetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    // t may e dead due to flips.
    if (isdead(&newtet)) continue;
    assert(!infected(newtet));
    // Check the four faces of t.
    for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
      tspivot(newtet, auxsh);
      if (auxsh.sh != dummysh) {
        // An auxilary subface s.
        assert(sorg(auxsh) == (point) NULL);
        tsdissolve(newtet);  // t -x-> s.
        sym(newtet, neightet);
        if (neightet.tet != dummytet) {
          assert(!isdead(&neightet));
          tsdissolve(neightet); // n -x-> s.
        }
        // Delete s.
        shellfacedealloc(subfaces, auxsh.sh);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// expandcavity()    Expand the cavity by adding new fronts.                 //
//                                                                           //
// This is the support routine for delaunizecavity().  Some fronts of C are  //
// missing in D since they're not strongly Delaunay. Such fronts are removed //
// and the faces of the tets abutting to them are added. C is then expanded. //
// Some removed faces may be subfaces, they're queued to recover later. D is //
// expanded simultaneously with the new vertices of the new fronts.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::expandcavity(list* frontlist, list* misfrontlist,
  list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque)
{
  triface misfront, newfront, casingtet, crosstet;
  triface searchtet, faketet, bakhulltet;
  face checksh;
  point pd;
  enum insertsiteresult success;
  long bakhullsize;
  int bakchksub;
  int i, j, k;

  if (b->verbose > 1) {
    printf("    Expand cavity (%d missing fronts).\n", misfrontlist->len());
  }
  // Increase the number of expanded times.
  expcavcount++;
  // The incrflipdelaunay() is re-used. Backup global variables.
  decode(dummytet[0], bakhulltet);
  bakhullsize = hullsize;
  bakchksub = checksubfaces;
  checksubfaces = 0;
  b->verbose--;

  // Choose a tet in D for searching.
  recenttet = * (triface *)(* newtetlist)[0];
  assert((recenttet.tet != dummytet) && !isdead(&recenttet));

  // Loop through 'misfrontlist'.
  for (i = 0; i < misfrontlist->len(); i++) {
    // Get a missing front f.
    misfront = * (triface *)(* misfrontlist)[i];
    // C will be expanded at f.
    if (b->verbose > 1) {
      printf("    Get misfront (%d, %d, %d).\n", pointmark(org(misfront)),
             pointmark(dest(misfront)), pointmark(apex(misfront)));
    }
    // Is f has a subface s?
    tspivot(misfront, checksh);
    if (checksh.sh != dummysh) {
      // A subface s is found. Check whether f is expandable at s.
      sym(misfront, crosstet);
      if (!infected(crosstet)) {
        // f is not expandable. In principle is should not happen. However,
        //   it can happen when PBC is in use.
        assert(checkpbcs);
        // Skip expanding f. It will be processed later.
        continue;
      }
      // Temporarily remove s. Queue and recover it later.
      if (b->verbose > 1) {
        printf("    Queuing subface (%d, %d, %d).\n",
               pointmark(sorg(checksh)), pointmark(sdest(checksh)),
               pointmark(sapex(checksh)));
      }
      // Detach s from tets at its both sides.
      tsdissolve(misfront);
      tsdissolve(crosstet);
      // Detach tets at from s.
      stdissolve(checksh);
      sesymself(checksh);
      stdissolve(checksh);
      // Mark and queue it.
      sinfect(checksh);
      missingshqueue->push(&checksh);
    }
    // f may already be processed (become a cross tet of C).
    if (infected(misfront)) continue;
    // Get the point p = oppo(t), t is the tet holds f.
    pd = oppo(misfront);
#ifdef SELF_CHECK
    // t must not be fake.
    assert(pd != (point) NULL);
#endif
    // Insert p in D. p may not be inserted if it is one of the two cases:
    //   (1) p is already a vertex of D;
    //   (2) p lies outside the CH of D;
    searchtet = recenttet;
    // Make sure the tet is valid (it may be killed by flips).
    if (isdead(&searchtet)) {
      // The tet is dead. Get a live tet in D. !!!
      for (j = 0; j < newtetlist->len(); j++) {
        recenttet = * (triface *)(* newtetlist)[j];
        if (!isdead(&recenttet)) break;
      }
      assert(j < newtetlist->len());
      searchtet = recenttet;
    }
    success = insertsite(pd, &searchtet, false, flipque);
    if (success == OUTSIDEPOINT) {
      // case (2). Insert p onto CH of D.
      inserthullsite(pd, &searchtet, flipque);
    }
    if (success != DUPLICATEPOINT) {
      // p is inserted. Recover Delaunness of D by flips.
      flip(flipque, NULL);
    }
    // Expand C by adding new fronts. The three faces of t which have p as a
    //   vertex become new fronts. However, if a new front is coincident with
    //   an old front of C, it is not added and the old front is removed. 
    adjustedgering(misfront, CCW);
    for (j = 0; j < 3; j++) {
      // Notice: Below I mis-used the names. 'newfront' is not exactly a new
      //   front, instead the 'casingtet' should be called new front. 
      // Get a new front f_n.
      fnext(misfront, newfront);
      // Get the neighbor tet n at f_n.
      sym(newfront, casingtet);
      // Is n a cross tet?
      if (!infected(casingtet)) {
        // f_n becomes a new front of C.
        // Does n exist?
        if (casingtet.tet == dummytet) {
          // Create a fake tet n' to hold f_n temporarily.
          maketetrahedron(&faketet);
          // Be sure that the vertices of fake tet are CCW oriented.
          adjustedgering(newfront, CW); // CW edge ring.
          setorg(faketet, org(newfront));
          setdest(faketet, dest(newfront));
          setapex(faketet, apex(newfront));
          setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
          // Bond n' to a subface if it exists.
          tspivot(newfront, checksh);
          if (checksh.sh != dummysh) {
            sesymself(checksh);
            tsbond(faketet, checksh);
          } 
          // Bond f_n <--> n'. So we're able to find n' and remove it.
          bond(faketet, newfront);
          frontlist->append(&faketet);
        } else {
          // Add n to frontlist.
          frontlist->append(&casingtet);
        }
      } else {
        // f_n is coincident with an existing front f' of C. f' is no longer
        //   a front, remove it from frontlist.  Use the inverse order to
        //   search f' (most likely, a newly added front may be f').
        for (k = frontlist->len() - 1; k >= 0; k--) {
          searchtet = * (triface *)(* frontlist)[k];
          if ((newfront.tet == searchtet.tet) &&
              (newfront.loc == searchtet.loc)) {
            frontlist->del(k, 0);
            break;
          }
        }
        // Is f_n a subface?
        tspivot(newfront, checksh);
        if (checksh.sh != dummysh) {
          // Temporarily remove checksh. Make it missing. recover it later.
          if (b->verbose > 2) {
            printf("    Queuing subface (%d, %d, %d).\n",
                   pointmark(sorg(checksh)), pointmark(sdest(checksh)),
                   pointmark(sapex(checksh)));
          }
          tsdissolve(newfront);
          tsdissolve(casingtet);
          // Detach tets at the both sides of checksh.
          stdissolve(checksh);
          sesymself(checksh);
          stdissolve(checksh);
          sinfect(checksh);
          missingshqueue->push(&checksh);
        }
      }
      enextself(misfront);
    }
    // C has been expanded at f. t becomes a cross tet.
    if (!infected(misfront)) {
      // t will be deleted, queue it.
      infect(misfront);
      crosstetlist->append(&misfront);
    }
  }

  // Loop through misfrontlist, remove infected misfronts.
  for (i = 0; i < misfrontlist->len(); i++) {
    misfront = * (triface *)(* misfrontlist)[i];
    if (infected(misfront)) {
      // Remove f, keep original list order.
      misfrontlist->del(i, 1);
      i--;
    }
  }

  // Are we done?
  if (misfrontlist->len() > 0) {
    // No. There are unexpandable fronts.
    // expandcavity_sos(misfrontlist);
    assert(0); // Not done yet.
  }

  // D has been updated (by added new tets or dead tets) (due to flips).
  retrievenewtets(newtetlist);

  // Restore global variables.
  dummytet[0] = encode(bakhulltet);
  hullsize = bakhullsize;
  checksubfaces = bakchksub;
  b->verbose++;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carvecavity()    Remove redundant (outside) tetrahedra from D.            //
//                                                                           //
// The fronts of C have been identified in D. Hence C can be tetrahedralized //
// by removing the tets outside C. The CDT is then updated by filling C with //
// the remaining tets (inside C) of D.                                       //
//                                                                           //
// Each front is protected by an auxilary subface s in D. s has a pointer to //
// f (s.sh[0]). f can be used to classified the in- and out- tets of C (the  //
// CW orientation of f faces to the inside of C). The classified out-tets of //
// C are marked (infected) for removing.                                     //
//                                                                           //
// Notice that the out-tets may not only the tets on the CH of C,  but also  //
// tets completely inside D, eg., there is a "hole" in D.  Such tets must be //
// marked during classification. The hole tets are poped up and removed too. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carvecavity(list* newtetlist, list* outtetlist,
  queue* flipque)
{
  triface newtet, neightet, front, outtet;
  face auxsh, consh;
  point pointptr;
  REAL ori;
  int i;

  // Clear work list.
  outtetlist->clear();

  // Classify in- and out- tets in D. Mark and queue classified out-tets.
  for (i = 0; i < newtetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    assert(!isdead(&newtet));
    // Look for aux subfaces attached at t.
    for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
      tspivot(newtet, auxsh);
      if (auxsh.sh != dummysh) {
        // Has this side a neighbor n? 
        sym(newtet, neightet);
        if (neightet.tet != dummytet) {
          // Classify t and n (one is "in" and another is "out").
          // Get the front f.
          decode((tetrahedron) auxsh.sh[0], front);
          // Let f face to the inside of C.
          adjustedgering(front, CW);
          ori = orient3d(org(front), dest(front), apex(front), oppo(newtet));
          assert(ori != 0.0);
          if (ori < 0.0) {
            // t is in-tet. n is out-tet.
            outtet = neightet;
          } else {
            // n is in-tet. t is out-tet.
            outtet = newtet;
          }
          // Add the out-tet into list.
          if (!infected(outtet)) {
            infect(outtet);
            outtetlist->append(&outtet);
          }
        }
      }
    }
  }

  // Find and mark all out-tets.
  for (i = 0; i < outtetlist->len(); i++) {
    outtet = * (triface *)(* outtetlist)[i];
    for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) {
      sym(outtet, neightet);
      // Does the neighbor exist and unmarked?
      if ((neightet.tet != dummytet) && !infected(neightet)) {
        // Is it protected by an aux subface?
        tspivot(outtet, auxsh);
        if (auxsh.sh == dummysh) {
          // It's an out-tet.
          infect(neightet);
          outtetlist->append(&neightet);
        }
      }
    }
  }

  // Remove the out- (and hole) tets.
  for (i = 0; i < outtetlist->len(); i++) {
    // Get an out-tet t.
    outtet = * (triface *)(* outtetlist)[i];
    // Detach t from the in-tets.
    for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) {
      // Is there an aux subface s?
      tspivot(outtet, auxsh);
      if (auxsh.sh != dummysh) {
        // Get the neighbor n.
        sym(outtet, neightet);
        assert(!infected(neightet)); // t must be in-tet.
        // Detach n -x-> t.
        dissolve(neightet);
      }
    }
    // Dealloc the tet.
    tetrahedrondealloc(outtet.tet);
  }

  // Connect the in-tets of C to fronts. Remove aux subfaces and fake tets.
  for (i = 0; i < newtetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    // t may be an out-tet and has got deleted.
    if (isdead(&newtet)) continue;
    // t is an in-tet. Look for aux subfaces attached at t.
    for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
      // Is there an aux subface s?
      tspivot(newtet, auxsh);
      if (auxsh.sh != dummysh) {
        // Get the front f.
        decode((tetrahedron) auxsh.sh[0], front);
        assert((front.tet != dummytet) && !infected(front));
        // s has fulfilled its duty. Can be deleted.
        tsdissolve(newtet); // dissolve: t -x-> s.
        // Delete s.
        shellfacedealloc(subfaces, auxsh.sh);
        // Connect the newtet t and front f.
        // Is there a concrete subface c at f.
        tspivot(front, consh);
        if (consh.sh != dummysh) {
          sesymself(consh);
          // Bond: t <--> c.
          tsbond(newtet, consh);
        }
        // Does f hold by a fake tet.
        if (oppo(front) == (point) NULL) {
          // f is fake.
          if (consh.sh != dummysh) {
            sesymself(consh);
            // Dissolve: c -x-> f.
            stdissolve(consh);
          }
          // Dealloc f.
          tetrahedrondealloc(front.tet);
          // f becomes a hull. let 'dummytet' bond to it.
          dummytet[0] = encode(newtet);
        } else {
          // Bond t <--> f.
          bond(newtet, front);
        }
        // t may be non-locally Delaunay and flipable.
        if (flipque != (queue *) NULL) {
          enqueueflipface(newtet, flipque);
        }
      }
    }
    // Let the corners of t2 point to it for fast searching.
    pointptr = org(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = dest(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = apex(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = oppo(newtet);
    setpoint2tet(pointptr, encode(newtet));
  }
  // The cavity has been re-tetrahedralized.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizecavity()    Tetrahedralize a cavity by Delaunay tetrahedra.      //
//                                                                           //
// The cavity C is bounded by a set of triangles in 'floorlist' (a list of   //
// coplanar subfaces) and 'ceillist' (a list of tetrahedral faces lie above  //
// the subfaces). 'floorptlist' and 'ceilptlist' are the vertices of C.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizecavity(list* floorlist, list* ceillist,
  list* ceilptlist, list* floorptlist, list* frontlist, list* misfrontlist,
  list* newtetlist, list* crosstetlist, queue* missingshqueue, queue* flipque)
{
  int vertnum;

  vertnum = floorptlist->len();
  vertnum += (ceilptlist != (list *) NULL ? ceilptlist->len() : 0);
  if (b->verbose > 1) {
    printf("    Delaunizing cavity (%d floors, %d ceilings, %d vertices).\n",
           floorlist->len(), ceillist->len(), vertnum);
  }
  // Save the size of the largest cavity.
  if ((floorlist->len() + ceillist->len()) > maxcavfaces) {
    maxcavfaces = floorlist->len() + ceillist->len();
  }
  if (vertnum > maxcavverts) {
    maxcavverts = vertnum;
  }

  // Clear these lists.
  frontlist->clear();
  misfrontlist->clear();
  newtetlist->clear();

  // Initialize the cavity C.
  initializecavity(floorlist, ceillist, frontlist);
  // Form the D of the vertices of C.
  delaunizecavvertices(NULL, floorptlist, ceilptlist, newtetlist, flipque);    
  // Identify faces of C in D.
  while (!identifyfronts(frontlist, misfrontlist, newtetlist)) {
    // Remove protecting subfaces, keep new tets.
    detachauxsubfaces(newtetlist);
    // Expand C and updateing D.
    expandcavity(frontlist, misfrontlist, newtetlist, crosstetlist,
                 missingshqueue, flipque);
  }
  // All fronts have identified in D. Get the shape of C by removing out
  //   tets of C. 'misfrontlist' is reused for removing out tets.
  carvecavity(newtetlist, misfrontlist, NULL);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formmissingregion()    Form the missing region.                           //
//                                                                           //
// 'missingsh' is a missing subface.  Start from it we can form the missing  //
// region R (a set of connected missing subfaces).  Because all missing sub- //
// faces have been marked (infected) before. R can be formed by checking the //
// neighbors of 'missingsh', and the neighbors of the neighbors, and so on.  //
// Stop checking further at either a segment or an unmarked subface.         //
//                                                                           //
// 'missingshlist' returns R. The edge ring of subfaces of R are oriented in //
// the same direction. 'equatptlist' returns the vertices of R, each vertex  //
// is marked with '1' (in 'worklist').                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formmissingregion(face* missingsh, list* missingshlist,
  list* equatptlist, int* worklist)
{
  face neighsh, worksh, workseg;
  point workpt[3];
  int idx, i, j;

  // Add 'missingsh' into 'missingshlist'.
  missingshlist->append(missingsh);
  // Save and mark its three vertices.
  workpt[0] = sorg(*missingsh);
  workpt[1] = sdest(*missingsh);
  workpt[2] = sapex(*missingsh);
  for (i = 0; i < 3; i++) {
    idx = pointmark(workpt[i]) - in->firstnumber;
    worklist[idx] = 1;
    equatptlist->append(&workpt[i]);
  }
  // Temporarily uninfect it (avoid to save it again).
  suninfect(*missingsh);
  
  // Find the other missing subfaces.
  for (i = 0; i < missingshlist->len(); i++) {
    // Get a missing subface.
    worksh = * (face *)(* missingshlist)[i];
    // Check three neighbors of this face.
    for (j = 0; j < 3; j++) {
      sspivot(worksh, workseg);
      if (workseg.sh == dummysh) {
        spivot(worksh, neighsh);
        if (sinfected(neighsh)) {
          // Find a missing subface, adjust the face orientation.
          if (sorg(neighsh) != sdest(worksh)) {
            sesymself(neighsh);
          }
          if (b->verbose > 2) {
            printf("    Add missing subface (%d, %d, %d).\n", 
                   pointmark(sorg(neighsh)), pointmark(sdest(neighsh)),
                   pointmark(sapex(neighsh)));
          }
          missingshlist->append(&neighsh);
          // Save and mark its apex.
          workpt[0] = sapex(neighsh);
          idx = pointmark(workpt[0]) - in->firstnumber;
          // Has workpt[0] been added?
          if (worklist[idx] == 0) {
            worklist[idx] = 1;
            equatptlist->append(&workpt[0]);
          }
          // Temporarily uninfect it (avoid to save it again).
          suninfect(neighsh);
        } 
      } 
      senextself(worksh);
    }
  }

  // R has been formed. Infect missing subfaces again.
  for (i = 0; i < missingshlist->len(); i++) {
    worksh = * (face *)(* missingshlist)[i];
    sinfect(worksh);
  } 
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// rearrangesubfaces()    Rearrange the set of subfaces of a missing region  //
//                        so that they conform to the faces of DT.           //
//                                                                           //
// The missing region formed by subfaces of 'missingshlist' contains a set   //
// of degenerate vertices, hence the set of subfaces don't match the set of  //
// faces in DT.  Instead of forcing them to present in DT, we re-arrange the //
// connection of them so that the new subfaces conform to the faces of DT.   //
// 'boundedgelist' is a set of boundary edges of the region, these edges(may //
// be subsegments) must exist in DT.                                         //
//                                                                           //
// On completion, we have created and inserted a set of new subfaces which   //
// conform to faces of DT. The set of old subfaces in 'missingshlist' are    //
// deleted. The region vertices in 'equatptlist' are unmarked.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::rearrangesubfaces(list* missingshlist, list* boundedgelist,
  list* equatptlist, int* worklist)
{
  link *boundedgelink;
  link *newshlink;
  triface starttet, spintet, neightet, worktet;
  face shloop, newsh, neighsh, spinsh, worksh;
  face workseg, casingin, casingout;
  point torg, tdest, workpt;
  point spt1, spt2, spt3;
  enum finddirectionresult collinear;
  enum shestype shtype;
  REAL area;
  bool matchflag, finishflag;
  int shmark, pbcgp, idx, hitbdry;
  int i, j;

  // Initialize the boundary edge link.
  boundedgelink = new link(sizeof(face), NULL, 256);
  // Initialize the new subface link.
  newshlink = new link(sizeof(face), NULL, 256);
  // Remember the type (skinny or not) of replaced subfaces.  They should
  //   all have the same type since there is no segment inside the region.
  worksh = * (face *)(* missingshlist)[0];
  shtype = shelltype(worksh);
  // The following loop is only for checking purpose.
  for (i = 1; i < missingshlist->len(); i++) {
    worksh = * (face *)(* missingshlist)[i];
    assert(shelltype(worksh) == shtype);
  }
  // To avoid compilation warnings.
  shmark = pbcgp = 0;
  area = 0.0; 

  // Create an initial boundary link.
  for (i = 0; i < boundedgelist->len(); i++) {
    shloop = * (face *)(* boundedgelist)[i];
    if (i == 0) {
      // 'shmark' will be set to all new created subfaces.
      shmark = shellmark(shloop);
      if (b->quality && varconstraint) {
        // area will be copied to all new created subfaces.
        area = areabound(shloop);
      }
      if (checkpbcs) {
        // pbcgp will be copied to all new created subfaces.
        pbcgp = shellpbcgroup(shloop);
      }
      // Get the abovepoint of this facet.
      abovepoint = facetabovepointarray[shellmark(shloop)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&shloop);
      }
    }
    sspivot(shloop, workseg);
    if (workseg.sh == dummysh) {
      // This edge is an interior edge.
      spivot(shloop, neighsh);
      boundedgelink->add(&neighsh);
    } else {
      // This side has a segment, the edge exists. 
      boundedgelink->add(&shloop);
    }
  }

  // Each edge ab of boundedgelink will be finished by finding a vertex c
  //   which is a vertex of the missing region, such that:
  //   (1) abc is inside the missing region, i.e., abc intersects at least
  //       one of missing subfaces (saved in missingshlist);
  //   (2) abc is not intersect with any previously created new subfaces
  //       in the missing region (saved in newshlink).
  //   After abc is created, it will be inserted into both the surface mesh
  //   and the DT. The boundedgelink will be updated, ab is removed, bc and
  //   ca will be added if they are open.

  while (boundedgelink->len() > 0) {
    // Remove an edge (ab) from the link.
    shloop = * (face *) boundedgelink->del(1);
    // 'workseg' indicates it is a segment or not.
    sspivot(shloop, workseg);
    torg = sorg(shloop);  // torg = a;
    tdest = sdest(shloop);  // tdest = b; 
    // Find a tetrahedron containing ab.
    getsearchtet(torg, tdest, &starttet, &workpt);
    collinear = finddirection(&starttet, workpt, tetrahedrons->items);
    if (collinear == LEFTCOLLINEAR) {
      enext2self(starttet);
      esymself(starttet);
    } else if (collinear == TOPCOLLINEAR) {
      fnextself(starttet);
      enext2self(starttet);
      esymself(starttet);
    }
    assert(dest(starttet) == workpt);
    // Checking faces around ab until a valid face is found.
    matchflag = false;
    spintet = starttet;
    hitbdry = 0;
    do {
      workpt = apex(spintet);
      idx = pointmark(workpt) - in->firstnumber;
      if (worklist[idx] == 1) {
        // (trog, tdest, workpt) is on the facet. Check if it satisfies (1).
        finishflag = false;
        for (i = 0; i < missingshlist->len(); i++) {
          worksh = * (face *)(* missingshlist)[i];
          spt1 = sorg(worksh);
          spt2 = sdest(worksh);
          spt3 = sapex(worksh);
          // Does bc intersect the face?
          if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint)
              == INTERSECT) {
            finishflag = true; break;
          }
          // Does ca intersect the face?
          if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint)
              == INTERSECT) {
            finishflag = true; break;
          }
          // Does c inside the face?
          if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint)
              == INTERSECT) {
            finishflag = true; break;
          }
        }
        if (finishflag) {
          // Satisfying (1). Check if it satisfies (2).
          matchflag = true;
          for (i = 0; i < newshlink->len() && matchflag; i++) {
            worksh = * (face *) newshlink->getnitem(i + 1);
            spt1 = sorg(worksh);
            spt2 = sdest(worksh);
            spt3 = sapex(worksh);
            // Does bc intersect the face?
            if (tri_edge_cop_inter(spt1, spt2, spt3, tdest, workpt, abovepoint)
                == INTERSECT) {
              matchflag = false; break;
            }
            // Does ca intersect the face?
            if (tri_edge_cop_inter(spt1, spt2, spt3, workpt, torg, abovepoint)
                == INTERSECT) {
              matchflag = false; break;
            }
            // Does c inside the face?
            if (tri_vert_cop_inter(spt1, spt2, spt3, workpt, abovepoint)
                == INTERSECT) {
              matchflag = false; break;
            }
          }
        }
        if (matchflag == true) {
          // Satisfying both (1) and (2). Find abc.
          break;
        }
      }
      if (!fnextself(spintet)) {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(starttet, spintet);
          if (!fnextself(spintet)) {
            hitbdry ++;
          }
        }
      }
    } while (hitbdry < 2 && apex(spintet) != apex(starttet));
    assert(matchflag == true);
    tspivot(spintet, neighsh);
    if (neighsh.sh != dummysh) {
      printf("Error:  Invalid PLC.\n");
      printf("  Facet #%d and facet #%d overlap each other.\n",
             shellmark(neighsh), shellmark(shloop));
      printf("  It might be caused by a facet is defined more than once.\n");
      printf("  Hint:  Use -d switch to find all overlapping facets.\n");
      exit(1);
    }
    // The side of 'spintet' is at which a new subface will be attached.
    adjustedgering(spintet, CCW);
    // Create the new subface.
    makeshellface(subfaces, &newsh);
    setsorg(newsh, org(spintet));
    setsdest(newsh, dest(spintet));
    setsapex(newsh, apex(spintet));
    if (b->quality && varconstraint) {
      setareabound(newsh, area);
    }
    if (checkpbcs) {
      setshellpbcgroup(newsh, pbcgp);
    }
    setshellmark(newsh, shmark);
    setshelltype(newsh, shtype);  // It may be a skinny subface.
    // Add newsh into newshlink for intersecting checking.
    newshlink->add(&newsh);
    // Insert it into the current mesh.
    tsbond(spintet, newsh);
    sym(spintet, neightet);
    if (neightet.tet != dummytet) {
      sesym(newsh, neighsh);
      tsbond(neightet, neighsh);
    }
    // Insert it into the surface mesh.
    sspivot(shloop, workseg);
    if (workseg.sh == dummysh) {
      sbond(shloop, newsh);
    } else {
      // There is a subsegment, 'shloop' is the subface which is going to
      //   die. Insert the 'newsh' at the place of 'shloop' into its face
      //   link, so as to dettach 'shloop'.   The original connection is:
      //   -> casingin -> shloop -> casingout ->, it will be changed with:
      //   -> casingin ->  newsh -> casingout ->.  Pay attention to the
      //   case when this subsegment is dangling in the mesh, i.e., 'shloop'
      //   is bonded to itself.
      spivot(shloop, casingout);
      if (shloop.sh != casingout.sh) {
        // 'shloop' is not bonded to itself.
        spinsh = casingout;
        do {
          casingin = spinsh;
          spivotself(spinsh);
        } while (sapex(spinsh) != sapex(shloop));
        assert(casingin.sh != shloop.sh); 
        // Bond casingin -> newsh -> casingout.
        sbond1(casingin, newsh);
        sbond1(newsh, casingout);
      } else {
        // Bond newsh -> newsh.
        sbond(newsh, newsh);
      }
      // Bond the segment.
      ssbond(newsh, workseg);
    }
    // Check other two sides of this new subface.  If a side is not bonded
    //   to any edge in the link, it will be added to the link.
    for (i = 0; i < 2; i++) {
      if (i == 0) {
        senext(newsh, worksh);
      } else {
        senext2(newsh, worksh);
      }
      torg = sorg(worksh);
      tdest = sdest(worksh);
      finishflag = false;
      for (j = 0; j < boundedgelink->len() && !finishflag; j++) {
        neighsh = * (face *) boundedgelink->getnitem(j + 1);
        if ((sorg(neighsh) == torg && sdest(neighsh) == tdest) ||
            (sorg(neighsh) == tdest && sdest(neighsh) == torg)) {
          // Find a boundary edge.  Bond them and exit the loop.
          sspivot(neighsh, workseg);
          if (workseg.sh == dummysh) {
            sbond(neighsh, worksh);
          } else {
            // There is a subsegment, 'neighsh' is the subface which is
            //   going to die. Do the same as above for 'worksh'.
            spivot(neighsh, casingout);
            if (neighsh.sh != casingout.sh) {
              // 'neighsh' is not bonded to itself.
              spinsh = casingout;
              do {
                casingin = spinsh;
                spivotself(spinsh);
              } while (sapex(spinsh) != sapex(neighsh));
              assert(casingin.sh != neighsh.sh); 
              // Bond casingin -> worksh -> casingout.
              sbond1(casingin, worksh);
              sbond1(worksh, casingout);
            } else {
              // Bond worksh -> worksh.
              sbond(worksh, worksh);
            }
            // Bond the segment.
            ssbond(worksh, workseg);
          }
          // Remove this boundary edge from the link.
          boundedgelink->del(j + 1);
          finishflag = true;
        }
      }
      if (!finishflag) {
        // It's a new boundary edge, add it to link.
        boundedgelink->add(&worksh);
      }
    }
  }

  // Deallocate the set of old missing subfaces.
  for (i = 0; i < missingshlist->len(); i++) {
    worksh = * (face *)(* missingshlist)[i];
    shellfacedealloc(subfaces, worksh.sh);
  }
  // Unmark region vertices in 'worklist'.
  for (i = 0; i < equatptlist->len(); i++) {
    workpt = * (point *)(* equatptlist)[i];
    idx = pointmark(workpt) - in->firstnumber;
    worklist[idx] = 0;
  }

  delete boundedgelink;
  delete newshlink;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutcrossingedge()    Search an edge crossing the missing region.        //
//                                                                           //
// 'missingshlist' forms the missing region R. This routine searches for an  //
// edge crossing R.  It first forms a 'boundedgelist' consisting of the      //
// boundary edges of R. Such edges are existing in CDT.  A crossing edge is  //
// found by rotating faces around one of the boundary edges. It is possible  //
// there is no edge crosses R (e.g. R has a degenerate point set).           //
//                                                                           //
// If find a croosing edge, return TRUE, 'crossedgelist' contains this edge. //
// Otherwise, return FALSE.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::scoutcrossingedge(list* missingshlist, list* boundedgelist,
  list* crossedgelist, int* worklist)
{
  triface starttet, spintet, worktet;
  face startsh, neighsh, worksh, workseg;
  point torg, tdest, tapex;
  point workpt[3], pa, pb, pc;
  enum finddirectionresult collinear;
  REAL ori1, ori2;
  bool crossflag;
  int hitbdry;
  int i, j, k;

  // Form the 'boundedgelist'. Loop through 'missingshlist', check each
  //   edge of these subfaces. If an edge is a segment or the neighbor
  //   subface is uninfected, add it to 'boundedgelist'.
  for (i = 0; i < missingshlist->len(); i++) {
    worksh = * (face *)(* missingshlist)[i];
    for (j = 0; j < 3; j++) {
      sspivot(worksh, workseg);
      if (workseg.sh == dummysh) {
        spivot(worksh, neighsh);
        if (!sinfected(neighsh)) {
          boundedgelist->append(&worksh);
        }
      } else {
        boundedgelist->append(&worksh);
      }
      senextself(worksh);
    }
  }

  crossflag = false;
  // Find a crossing edge. It is possible there is no such edge. We need to
  //   loop through all edges of 'boundedgelist' for sure we don't miss any.
  for (i = 0; i < boundedgelist->len() && !crossflag; i++) {
    startsh = * (face *)(* boundedgelist)[i];
    // 'startsh' (abc) holds an existing edge of the DT, find it.
    torg = sorg(startsh);
    tdest = sdest(startsh);
    tapex = sapex(startsh);
    getsearchtet(torg, tdest, &starttet, &workpt[0]);
    collinear = finddirection(&starttet, workpt[0], tetrahedrons->items);
    if (collinear == LEFTCOLLINEAR) {
      enext2self(starttet);
      esymself(starttet);
    } else if (collinear == TOPCOLLINEAR) {
      fnextself(starttet);
      enext2self(starttet);
      esymself(starttet);
    }
#ifdef SELF_CHECK
    assert(dest(starttet) == workpt[0]);
#endif
    // Now starttet holds edge ab. Find is edge de crossing R.
    spintet = starttet;
    hitbdry = 0;
    do {
      if (fnextself(spintet)) {
        // splittet = abde. Check if de crosses abc.
        workpt[1] = apex(spintet);  // workpt[1] = d.
        workpt[2] = oppo(spintet);  // workpt[2] = e.
        j = pointmark(workpt[1]) - in->firstnumber;
        k = pointmark(workpt[2]) - in->firstnumber;
        if (worklist[j] == 1) {
          ori1 = 0.0; // d is a vertex of the missing region.
        } else {
          // Get the orientation of d wrt. abc.
          ori1 = orient3d(torg, tdest, tapex, workpt[1]);
        }
        if (worklist[k] == 1) {
          ori2 = 0.0; // e is a vertex of the missing region.
        } else {
          // Get the orientation of e wrt. abc.
          ori2 = orient3d(torg, tdest, tapex, workpt[2]);
        }
        // Only do check if d and e locate on different sides of abc.
        if (ori1 * ori2 < 0.0) {
          // Check if de crosses any subface of R.
          for (j = 0; j < missingshlist->len(); j++) {
            worksh = * (face *)(* missingshlist)[j];
            pa = sorg(worksh);
            pb = sdest(worksh);
            pc = sapex(worksh);
            crossflag = (tri_tri_inter(pa, pb, pc, workpt[0], workpt[1],
                                       workpt[2]) == INTERSECT);
            if (crossflag) {
              // Find a crossing edge. We're done.
              worktet = spintet;
              adjustedgering(worktet, CCW);
              enextfnextself(worktet);
              enextself(worktet);
              // Add this edge (worktet) into 'crossedgelist'.
              crossedgelist->append(&worktet);
              break;
            }
          }
          if (crossflag) break;
        }
        if (apex(spintet) == apex(starttet)) break;
      } else {
        hitbdry++;
        // It is only possible to hit boundary once.
        if (hitbdry < 2) {
          esym(starttet, spintet);
        }
      }
    } while (hitbdry < 2);
  }

  return crossflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formcavity()    Form the cavity for recovering the missing region.        //
//                                                                           //
// The cavity C is bounded by faces of current CDT.  All tetrahedra inside C //
// will be removed, intead a set of constrained Delaunay tetrahedra will be  //
// filled in and the missing region are recovered.                           //
//                                                                           //
// 'missingshlist' contains a set of subfaces forming the missing region R.  //
// C is formed by first finding all the tetrahedra in CDT that intersect the //
// relative interior of R; then deleting them from the CDT, this will form C //
// inside the CDT. At the beginning, 'crossedgelist' contains an edge which  //
// is crossing R. All tets containing this edge must cross R. Start from it, //
// other crossing edges can be found incrementally.  The discovered crossing //
// tets are saved in 'crosstetlist'.                                         //
//                                                                           //
// Notice that not all tets in 'crosstetlist' are crossing R. The discovered //
// tets are connected each other. However, there may be other tets crossing  //
// R but disjoint with the found tets. Due to this fact we need to check the //
// 'missingshlist' once more. Only recover those subfaces which are crossed  //
// by the set of discovered tets, i.e., R may be shrinked to conform the set //
// of discovered tets. The extra subfaces of R will be recovered later.      //
//                                                                           //
// Notice that some previous recovered subfaces may completely included in C.//
// This can happen when R is very big and these subfaces lie above R and so  //
// close to it. Such subfaces have to be queued (and sinfected()) to recover //
// them later. Otherwise, we lost the connection to these subfaces forever.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formcavity(list* missingshlist, list* crossedgelist,
  list* equatptlist, list* crossshlist, list* crosstetlist,
  list* belowfacelist, list* abovefacelist, list* horizptlist,
  list* belowptlist, list* aboveptlist, queue* missingshqueue, int* worklist)
{
  triface starttet, spintet, neightet, worktet;
  face startsh, neighsh, worksh, workseg;
  point torg, tdest, tapex, workpt[3];
  REAL checksign, orgori, destori;
  bool crossflag, inlistflag;
  bool belowflag, aboveflag;
  int idx, share;
  int i, j, k;

  // Get a face at horizon.
  startsh = * (face *)(* missingshlist)[0];
  torg = sorg(startsh);
  tdest = sdest(startsh);
  tapex = sapex(startsh);

  // Collect the set of crossing tetrahedra by rotating crossing edges.
  for (i = 0; i < crossedgelist->len(); i++) {
    // Get a tet abcd, ab is a crossing edge.
    starttet = * (triface *)(* crossedgelist)[i];
    adjustedgering(starttet, CCW);
    if (b->verbose > 2) {
      printf("    Collect tets containing edge (%d, %d).\n",
             pointmark(org(starttet)), pointmark(dest(starttet)));
    }
    orgori = orient3d(torg, tdest, tapex, org(starttet));
    destori = orient3d(torg, tdest, tapex, dest(starttet));
#ifdef SELF_CHECK
    assert(orgori * destori < 0.0); 
#endif
    spintet = starttet;
    do {
      // The face rotation should not meet boundary.
      fnextself(spintet); 
      // Check the validity of the PLC.
      tspivot(spintet, worksh);
      if (worksh.sh != dummysh) {
        printf("Error:  Invalid PLC.\n");
        printf("  Two subfaces (%d, %d, %d) and (%d, %d, %d)\n",
               pointmark(torg), pointmark(tdest), pointmark(tapex),
               pointmark(sorg(worksh)), pointmark(sdest(worksh)),
               pointmark(sapex(worksh)));
        printf("  are found intersecting each other.\n");
        printf("  Hint:  Use -d switch to find all intersecting facets.\n");
        terminatetetgen(1);
      }
      if (!infected(spintet)) {
        if (b->verbose > 2) {
          printf("      Add crossing tet (%d, %d, %d, %d).\n",
                 pointmark(org(spintet)), pointmark(dest(spintet)),
                 pointmark(apex(spintet)), pointmark(oppo(spintet)));
        }
        infect(spintet);
        crosstetlist->append(&spintet);
      }
      // Check whether other two edges of 'spintet' is a crossing edge.
      //   It can be quickly checked from the apex of 'spintet', if it is
      //   not on the facet, then there exists a crossing edge.
      workpt[0] = apex(spintet);
      idx = pointmark(workpt[0]) - in->firstnumber;
      if (worklist[idx] != 1) {
        // Either edge (dest, apex) or edge (apex, org) crosses.
        checksign = orient3d(torg, tdest, tapex, workpt[0]);
#ifdef SELF_CHECK
        assert(checksign != 0.0);
#endif
        if (checksign * orgori < 0.0) {
          enext2(spintet, worktet); // edge (apex, org).
          workpt[1] = org(spintet);
        } else {
#ifdef SELF_CHECK
          assert(checksign * destori < 0.0);
#endif
          enext(spintet, worktet);  // edge (dest, apex).
          workpt[1] = dest(spintet);
        }
        // 'worktet' represents the crossing edge. Add it into list only
        //   it doesn't exist in 'crossedgelist'.
        inlistflag = false;
        for (j = 0; j < crossedgelist->len() && !inlistflag; j++) {
          neightet = * (triface *)(* crossedgelist)[j];
          if (org(neightet) == workpt[0]) {
            if (dest(neightet) == workpt[1]) inlistflag = true;
          } else if (org(neightet) == workpt[1]) {
            if (dest(neightet) == workpt[0]) inlistflag = true;
          }
        }
        if (!inlistflag) {
          crossedgelist->append(&worktet);
        }
      }
    } while (apex(spintet) != apex(starttet));
  }

  // Identifying the boundary faces and vertices of C. Sort them into
  //   'abovefacelist', 'aboveptlist, 'belowfacelist', and 'belowptlist',
  //    respectively. "above" and "below" are wrt.(torg, tdest, tapex). 
  for (i = 0; i < crosstetlist->len(); i++) {
    // Get a tet abcd, ab is the crossing edge.
    starttet = * (triface *)(* crosstetlist)[i];
#ifdef SELF_CHECK
    assert(infected(starttet));
#endif
    adjustedgering(starttet, CCW);
    // abc and abd are sharing the crossing edge, the two neighbors must
    //   be crossing tetrahedra too. They can't be boundaries of C.
    for (j = 0; j < 2; j++) {
      if (j == 0) {
        enextfnext(starttet, worktet); // Check bcd.
      } else {
        enext2fnext(starttet, worktet); // Check acd. 
      } 
      sym(worktet, neightet);
      // If the neighbor doesn't exist or exists but doesn't be infected,
      //   it's a boundary face of C, save it.
      if ((neightet.tet == dummytet) || !infected(neightet)) {
        workpt[0] = org(worktet);
        workpt[1] = dest(worktet);
        workpt[2] = apex(worktet);
        belowflag = aboveflag = false;
        share = 0;
        for (k = 0; k < 3; k++) {
          idx = pointmark(workpt[k]) - in->firstnumber;
          if (worklist[idx] == 0) {
            // It's not a vertices of facet, find which side it lies.
            checksign = orient3d(torg, tdest, tapex, workpt[k]);
#ifdef SELF_CHECK
            assert(checksign != 0.0);
#endif
            if (checksign > 0.0) {
              // It lies "below" the facet wrt. 'startsh'.
              worklist[idx] = 2;
              belowptlist->append(&workpt[k]);
            } else if (checksign < 0.0) {
              // It lies "above" the facet wrt. 'startsh'.
              worklist[idx] = 3;
              aboveptlist->append(&workpt[k]);
            }
          }
          if (worklist[idx] == 2) {
            // This face lies "below" the facet wrt. 'startsh'.
            belowflag = true;
          } else if (worklist[idx] == 3) {
            // This face lies "above" the facet wrt. 'startsh'.
            aboveflag = true;
          } else {
#ifdef SELF_CHECK
            // In degenerate case, this face may just be the equator.
            assert(worklist[idx] == 1);
#endif
            share++;
          }
        }
#ifdef SELF_CHECK
        // The degenerate case has been ruled out.
        assert(share < 3);
        // Only one flag is possible for a cavity face.
        assert(belowflag ^ aboveflag); 
#endif
        if (belowflag) {
          belowfacelist->append(&worktet);
        } else if (aboveflag) {
          abovefacelist->append(&worktet);
        }
      }
    }
  }

  // Shrink R if not all its subfaces are crossing by the discovered tets.
  //   'crossshlist' and 'horizptlist' represent the set of subfaces and
  //   vertices of the shrinked missing region, respectively.
  for (i = 0; i < missingshlist->len(); i++) {
    worksh = * (face *)(* missingshlist)[i];
#ifdef SELF_CHECK
    assert(sinfected(worksh));
#endif
    workpt[0] = sorg(worksh);
    workpt[1] = sdest(worksh);
    workpt[2] = sapex(worksh);
    crossflag = false;
    for (j = 0; j < crosstetlist->len() && !crossflag; j++) {
      // Get a tet abcd, ab is a crossing edge.
      starttet = * (triface *)(* crosstetlist)[j];
      adjustedgering(starttet, CCW);
      // Only need to check two sides of worktet.
      for (k = 0; k < 2 && !crossflag; k++) {
        if (k == 0) {
          worktet = starttet; // Check abc.
        } else {
          fnext(starttet, worktet); // Check abd.
        }
        crossflag = tritritest(&worktet, workpt[0], workpt[1], workpt[2]);
      }
    }
    if (crossflag) {
      // 'worksh' is crossed by 'worktet', uninfect it.
      suninfect(worksh);
      crossshlist->append(&worksh);
      // Add its corners into 'horizptlist'.
      for (k = 0; k < 3; k++) {
        idx = pointmark(workpt[k]) - in->firstnumber;
        if (worklist[idx] != 4) {
          worklist[idx] = 4;
          horizptlist->append(&workpt[k]);
        }
      }
    } 
  }

  // Check 'crossingtetlist'. Queue subfaces inside them.
  for (i = 0; i < crosstetlist->len(); i++) {
    starttet = * (triface *)(* crosstetlist)[i];
    for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
      sym(starttet, neightet);
      // If the neighbor exist and is infected, check it.
      if ((neightet.tet != dummytet) && infected(neightet)) {
        tspivot(starttet, worksh);
        if (worksh.sh != dummysh) {
          // Temporarily remove worksh. Make it missing. recover it later.
          if (b->verbose > 2) {
            printf("    Queuing subface (%d, %d, %d).\n",
                   pointmark(sorg(worksh)), pointmark(sdest(worksh)),
                   pointmark(sapex(worksh)));
          }
          tsdissolve(neightet);
          tsdissolve(starttet);
          // Detach tets at the both sides of this subface.
          stdissolve(worksh);
          sesymself(worksh);
          stdissolve(worksh);
          sinfect(worksh);
          missingshqueue->push(&worksh);
        }
      }
    }
  }

  // Clear flags set in 'worklist'.
  for (i = 0; i < equatptlist->len(); i++) {
    workpt[0] = * (point *)(* equatptlist)[i];
    idx = pointmark(workpt[0]) - in->firstnumber;
#ifdef SELF_CHECK
    assert((worklist[idx] == 1) || (worklist[idx] == 4));
#endif
    worklist[idx] = 0;
  }
  for (i = 0; i < belowptlist->len(); i++) {
    workpt[0] = * (point *)(* belowptlist)[i];
    idx = pointmark(workpt[0]) - in->firstnumber;
#ifdef SELF_CHECK
    assert(worklist[idx] == 2);
#endif
    worklist[idx] = 0;
  }
  for (i = 0; i < aboveptlist->len(); i++) {
    workpt[0] = * (point *)(* aboveptlist)[i];
    idx = pointmark(workpt[0]) - in->firstnumber;
#ifdef SELF_CHECK
    assert(worklist[idx] == 3);
#endif
    worklist[idx] = 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertallsubfaces()    Insert all subfaces, queue missing subfaces.       //
//                                                                           //
// Loop through all subfaces, insert each into the DT. If one already exists,//
// bond it to the tetrahedra having it. Otherwise, it is missing, infect it  //
// and save it in 'missingshqueue'.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertallsubfaces(queue* missingshqueue)
{
  triface searchtet;
  face subloop;

  searchtet.tet = (tetrahedron *) NULL;
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    if (!insertsubface(&subloop, &searchtet)) {
      if (b->verbose > 1) {
        printf("    Queuing subface (%d, %d, %d).\n", pointmark(sorg(subloop)),
               pointmark(sdest(subloop)), pointmark(sapex(subloop)));
      }
      sinfect(subloop);
      missingshqueue->push(&subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedfacets()    Recover subfaces in a Delaunay tetrahedralization. //
//                                                                           //
// This routine creates a CDT by incrementally updating a DT D into a CDT T. //
// The process of recovering facets can be imagined by "merging" the surface //
// mesh F into D. At the beginning, F and D are completely seperated.  Some  //
// faces of them are matching some are not because they are crossed by some  //
// tetrahedra of D. The non-matching subfaces will be forced to appear in T  //
// by locally retetrahedralizing the regions where F and D are intersecting. //
//                                                                           //
// When a subface s of F is found missing in D, probably some other subfaces //
// near to s are missing too.  The set of adjoining coplanar missing faces   //
// forms a missing region R (R may not simply connected).                    //
//                                                                           //
// There are two possibilities can result a mssing region R: (1) Some edges  //
// of D cross R; (2) No edge of D crosses R, but some faces of D spans R, ie,//
// D is locally degenerate at R. In case (1), D is modified so that it resp- //
// ects R (done by a cavity retetrahedralization algorithm).  In case (2), F //
// is modified so that the set of subfaces of R matches faces in D (done by  //
// a face rearrangment algorithm).                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constrainedfacets()
{
  queue *missingshqueue, *flipque;
  list *missingshlist, *equatptlist;
  list *boundedgelist, *crossedgelist, *crosstetlist;
  list *crossshlist, *belowfacelist, *abovefacelist;
  list *horizptlist, *belowptlist, *aboveptlist;
  list *frontlist, *misfrontlist, *newtetlist;
  triface searchtet, worktet;
  face subloop, worksh;
  int *worklist;
  int i;

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

  // Initialize queues.
  missingshqueue = new queue(sizeof(face));
  flipque = new queue(sizeof(badface));
  // Initialize the working lists.
  missingshlist = new list(sizeof(face), NULL);
  boundedgelist = new list(sizeof(face), NULL);
  crossedgelist = new list(sizeof(triface), NULL);
  equatptlist = new list("point *");
  crossshlist = new list(sizeof(face), NULL);
  crosstetlist = new list(sizeof(triface), NULL);
  belowfacelist = new list(sizeof(triface), NULL);
  abovefacelist = new list(sizeof(triface), NULL);
  horizptlist = new list("point *");
  belowptlist = new list("point *");
  aboveptlist = new list("point *");
  frontlist = new list(sizeof(triface), NULL);
  misfrontlist = new list(sizeof(triface), NULL);
  newtetlist = new list(sizeof(triface), NULL);
  // Initialize the array for marking vertices.
  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 0;

  // Compute a mapping from points to tetrahedra for fast searching.
  makepoint2tetmap();
  
  // Match subfaces in D, queue all missing subfaces.
  insertallsubfaces(missingshqueue);

  // Recover all missing subfaces.
  while (!missingshqueue->empty()) {
    // Get a queued face s.
    subloop = * (face *) missingshqueue->pop();
    // s may have been deleted in a face rearrangment operation.
    if (isdead(&subloop)) continue;
    // s may have been recovered in a previous missing region.
    if (!sinfected(subloop)) continue;
    // s may match a face in D now due to previous transformations.
    if (insertsubface(&subloop, &searchtet)) {
      suninfect(subloop);
      continue;
    }
    if (b->verbose > 1) {
      printf("    Recover subface (%d, %d, %d).\n", pointmark(sorg(subloop)),
             pointmark(sdest(subloop)), pointmark(sapex(subloop)));
    }
    // Form the missing region R containing s.
    formmissingregion(&subloop, missingshlist, equatptlist, worklist);
    // Is R crossing by any tetrahedron?
    if (scoutcrossingedge(missingshlist, boundedgelist, crossedgelist,
                          worklist)) {
      // Form the cavity C containing R.
      formcavity(missingshlist, crossedgelist, equatptlist, crossshlist,
                 crosstetlist, belowfacelist, abovefacelist, horizptlist,
                 belowptlist, aboveptlist, missingshqueue, worklist);
      // Recover the above part of C.
      delaunizecavity(crossshlist, abovefacelist, aboveptlist, horizptlist,
                      frontlist, misfrontlist, newtetlist, crosstetlist,
                      missingshqueue, flipque);
      // Inverse the direction of subfaces in R.
      for (i = 0; i < crossshlist->len(); i++) {
        worksh = * (face *)(* crossshlist)[i];
        sesymself(worksh);
        * (face *)(* crossshlist)[i] = worksh;
      }
      // Recover the below part of C.
      delaunizecavity(crossshlist, belowfacelist, belowptlist, horizptlist,
                      frontlist, misfrontlist, newtetlist, crosstetlist,
                      missingshqueue, flipque);
      // Delete tetrahedra in C.
      for (i = 0; i < crosstetlist->len(); i++) {
        worktet = * (triface *)(* crosstetlist)[i];
        tetrahedrondealloc(worktet.tet);
      }
      // There may have some un-recovered subfaces of R. Put them back into
      //   queue. Otherwise, they will be missing on the boundary.
      for (i = 0; i < missingshlist->len(); i++) {
        worksh = * (face *)(* missingshlist)[i];
        if (sinfected(worksh)) {
          // An unrecovered subface, put it back into queue.
          missingshqueue->push(&worksh);
        }
      }
      crossshlist->clear();
      belowfacelist->clear();
      abovefacelist->clear();
      horizptlist->clear();
      belowptlist->clear();
      aboveptlist->clear();
      crosstetlist->clear();
    } else {
      // No. Rearrange subfaces of F conforming to that of D in R. It can
      //   happen when the facet has non-coplanar vertices.
      rearrangesubfaces(missingshlist, boundedgelist, equatptlist, worklist);
    }
    // Clear all working lists.
    missingshlist->clear();
    boundedgelist->clear();
    crossedgelist->clear();
    equatptlist->clear();
  }

  // Subfaces have been merged into D.
  checksubfaces = 1;

  if (b->verbose > 0) {
    printf("  The biggest cavity: %d faces, %d vertices\n", maxcavfaces,
           maxcavverts);
    printf("  Enlarged %d times\n", expcavcount);
  }

  delete missingshqueue;
  delete flipque;
  delete missingshlist;
  delete boundedgelist;
  delete crossedgelist;
  delete equatptlist;
  delete crossshlist;
  delete crosstetlist;
  delete belowfacelist;
  delete abovefacelist;
  delete horizptlist;
  delete belowptlist;
  delete aboveptlist;
  delete frontlist;
  delete misfrontlist;
  delete newtetlist;
  delete [] worklist;
}

//
// End of facet recovery routines
//

//
// Begin of carving out holes and concavities routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// infecthull()    Virally infect all of the tetrahedra of the convex hull   //
//                 that are not protected by subfaces.  Where there are      //
//                 subfaces, set boundary markers as appropriate.            //
//                                                                           //
// Memorypool 'viri' is used to return all the infected tetrahedra.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::infecthull(memorypool *viri)
{
  triface tetloop, tsymtet;
  tetrahedron **deadtet;
  face hullface;
  // point horg, hdest, hapex;

  if (b->verbose > 0) {
    printf("  Marking concavities for elimination.\n");
  }
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Is this tetrahedron on the hull?
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, tsymtet);
      if (tsymtet.tet == dummytet) {
        // Is the tetrahedron protected by a subface?
        tspivot(tetloop, hullface);
        if (hullface.sh == dummysh) {
          // The tetrahedron is not protected; infect it.
          if (!infected(tetloop)) {
            infect(tetloop);
            deadtet = (tetrahedron **) viri->alloc();
            *deadtet = tetloop.tet;
            break;  // Go and get next tet.
          }
        } else {
          // The tetrahedron is protected; set boundary markers if appropriate.
          if (shellmark(hullface) == 0) {
            setshellmark(hullface, 1);
            /*
            horg = sorg(hullface);
            hdest = sdest(hullface);
            hapex = sapex(hullface);
            if (pointmark(horg) == 0) {
              setpointmark(horg, 1);
            }
            if (pointmark(hdest) == 0) {
              setpointmark(hdest, 1);
            }
            if (pointmark(hapex) == 0) {
              setpointmark(hapex, 1);
            }
            */
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// plague()    Spread the virus from all infected tets to any neighbors not  //
//             protected by subfaces.                                        //
//                                                                           //
// This routine identifies all the tetrahedra that will die, and marks them  //
// as infected.  They are marked to ensure that each tetrahedron is added to //
// the virus pool only once, so the procedure will terminate. 'viri' returns //
// all infected tetrahedra which are outside the domian.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::plague(memorypool *viri)
{
  tetrahedron **virusloop;
  tetrahedron **deadtet;
  triface testtet, neighbor;
  face neighsh, testseg;
  face spinsh, casingin, casingout;
  int firstdadsub;
  int i;

  if (b->verbose > 0) {
    printf("  Marking neighbors of marked tetrahedra.\n");
  }
  firstdadsub = 0;
  // Loop through all the infected tetrahedra, spreading the virus to
  //   their neighbors, then to their neighbors' neighbors.
  viri->traversalinit();
  virusloop = (tetrahedron **) viri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Temporarily uninfect this tetrahedron, not necessary.
    uninfect(testtet);
    // Check each of the tetrahedron's four neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      // Find the neighbor.
      sym(testtet, neighbor);
      // Check for a shell between the tetrahedron and its neighbor.
      tspivot(testtet, neighsh);
      // Check if the neighbor is nonexistent or already infected.
      if ((neighbor.tet == dummytet) || infected(neighbor)) {
        if (neighsh.sh != dummysh) {
          // There is a subface separating the tetrahedron from its neighbor,
          //   but both tetrahedra are dying, so the subface dies too.
          // Before deallocte this subface, dissolve the connections between
          //   other subfaces, subsegments and tetrahedra.
          neighsh.shver = 0;
          if (!firstdadsub) {
            firstdadsub = 1; // Report the problem once.
            if (!b->quiet) {
              printf("Warning:  Detecting an open face (%d, %d, %d).\n",
                     pointmark(sorg(neighsh)), pointmark(sdest(neighsh)),
                     pointmark(sapex(neighsh)));
            }
          }
          // For keep the same enext() direction.
          findedge(&testtet, sorg(neighsh), sdest(neighsh));
          for (i = 0; i < 3; i++) {
            sspivot(neighsh, testseg);
            if (testseg.sh != dummysh) {
              // A subsegment is found at this side, dissolve this subface
              //   from the face link of this subsegment.
              testseg.shver = 0;
              spinsh = neighsh;
              if (sorg(spinsh) != sorg(testseg)) {
                sesymself(spinsh);
              }
              spivot(spinsh, casingout);
              if (casingout.sh == spinsh.sh) {
                // This is a trivial face link, only 'neighsh' itself,
                //   the subsegment at this side is also died.
                shellfacedealloc(subsegs, testseg.sh);
              } else {
                spinsh = casingout;
                do {
                  casingin = spinsh;
                  spivotself(spinsh);
                } while (spinsh.sh != neighsh.sh);
                // Set the link casingin->casingout.
                sbond1(casingin, casingout);
                // Bond the subsegment anyway.
                ssbond(casingin, testseg);
              }
            }
            senextself(neighsh);
            enextself(testtet);
          }
          if (neighbor.tet != dummytet) {
            // Make sure the subface doesn't get deallocated again later
            //   when the infected neighbor is visited.
            tsdissolve(neighbor);
          }
          // This subface has been separated.
          if (in->mesh_dim > 2) {
            shellfacedealloc(subfaces, neighsh.sh);
          } else {
            // Dimension is 2. keep it for output.
            // Dissolve tets at both sides of this subface.
            stdissolve(neighsh);
            sesymself(neighsh);
            stdissolve(neighsh);
          }
        }
      } else {                   // The neighbor exists and is not infected.
        if (neighsh.sh == dummysh) {
          // There is no subface protecting the neighbor, infect it.
          infect(neighbor);
          // Ensure that the neighbor's neighbors will be infected.
          deadtet = (tetrahedron **) viri->alloc();
          *deadtet = neighbor.tet;
        } else {               // The neighbor is protected by a subface.
          // Remove this tetrahedron from the subface.
          stdissolve(neighsh);
          // The subface becomes a boundary.  Set markers accordingly.
          if (shellmark(neighsh) == 0) {
            setshellmark(neighsh, 1);
          }
          // This side becomes hull. Update the handle in dummytet.
          dummytet[0] = encode(neighbor);
        }
      }
    }
    // Remark the tetrahedron as infected, so it doesn't get added to the
    //   virus pool again.
    infect(testtet);
    virusloop = (tetrahedron **) viri->traverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// regionplague()    Spread regional attributes and/or volume constraints    //
//                   (from a .poly file) throughout the mesh.                //
//                                                                           //
// This procedure operates in two phases.  The first phase spreads an attri- //
// bute and/or a volume constraint through a (facet-bounded) region.  The    //
// second phase uninfects all infected tetrahedra, returning them to normal. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
regionplague(memorypool *regionviri, REAL attribute, REAL volume)
{
  tetrahedron **virusloop;
  tetrahedron **regiontet;
  triface testtet, neighbor;
  face neighsh;

  if (b->verbose > 1) {
    printf("  Marking neighbors of marked tetrahedra.\n");
  }
  // Loop through all the infected tetrahedra, spreading the attribute
  //   and/or volume constraint to their neighbors, then to their neighbors'
  //   neighbors.
  regionviri->traversalinit();
  virusloop = (tetrahedron **) regionviri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Temporarily uninfect this tetrahedron, not necessary.
    uninfect(testtet);
    if (b->regionattrib) {
      // Set an attribute.
      setelemattribute(testtet.tet, in->numberoftetrahedronattributes,
                       attribute);
    }
    if (b->varvolume) {
      // Set a volume constraint.
      setvolumebound(testtet.tet, volume);
    }
    // Check each of the tetrahedron's four neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      // Find the neighbor.
      sym(testtet, neighbor);
      // Check for a subface between the tetrahedron and its neighbor.
      tspivot(testtet, neighsh);
      // Make sure the neighbor exists, is not already infected, and
      //   isn't protected by a subface, or is protected by a nonsolid
      //   subface.
      if ((neighbor.tet != dummytet) && !infected(neighbor)
          && (neighsh.sh == dummysh)) {
        // Infect the neighbor.
        infect(neighbor);
        // Ensure that the neighbor's neighbors will be infected.
        regiontet = (tetrahedron **) regionviri->alloc();
        *regiontet = neighbor.tet;
      }
    }
    // Remark the tetrahedron as infected, so it doesn't get added to the
    //   virus pool again.
    infect(testtet);
    virusloop = (tetrahedron **) regionviri->traverse();
  }

  // Uninfect all tetrahedra.
  if (b->verbose > 1) {
    printf("  Unmarking marked tetrahedra.\n");
  }
  regionviri->traversalinit();
  virusloop = (tetrahedron **) regionviri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    uninfect(testtet);
    virusloop = (tetrahedron **) regionviri->traverse();
  }
  // Empty the virus pool.
  regionviri->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeholetets()    Remove tetrahedra which are outside the domain.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::removeholetets(memorypool* viri)
{
  tetrahedron **virusloop;
  triface testtet, neighbor;
  point checkpt;
  int *tetspernodelist;
  int i, j;

  if (b->verbose > 0) {
    printf("  Deleting marked tetrahedra.\n");
  }

  // Create and initialize 'tetspernodelist'.
  tetspernodelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0;
  
  // Loop the tetrahedra list, counter the number of tets sharing each node.
  tetrahedrons->traversalinit();
  testtet.tet = tetrahedrontraverse();
  while (testtet.tet != (tetrahedron *) NULL) {
    // Increment the number of sharing tets for each endpoint.
    for (i = 0; i < 4; i++) {
      j = pointmark((point) testtet.tet[4 + i]);
      tetspernodelist[j]++;
    }
    testtet.tet = tetrahedrontraverse();
  }

  viri->traversalinit();
  virusloop = (tetrahedron **) viri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Record changes in the number of boundary faces, and disconnect
    //   dead tetrahedra from their neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      sym(testtet, neighbor);
      if (neighbor.tet == dummytet) {
        // There is no neighboring tetrahedron on this face, so this face
        //   is a boundary face.  This tetrahedron is being deleted, so this
        //   boundary face is deleted.
        hullsize--;
      } else {
        // Disconnect the tetrahedron from its neighbor.
        dissolve(neighbor);
        // There is a neighboring tetrahedron on this face, so this face
        //   becomes a boundary face when this tetrahedron is deleted.
        hullsize++;
      }
    }
    // Check the four corners of this tet if they're isolated.
    for (i = 0; i < 4; i++) {
      checkpt = (point) testtet.tet[4 + i];
      j = pointmark(checkpt);
      tetspernodelist[j]--;
      if (tetspernodelist[j] == 0) {
        // If it is added volume vertex or '-j' is not used, delete it.
        if ((pointtype(checkpt) == FREEVOLVERTEX) || !b->nojettison) { 
          setpointtype(checkpt, UNUSEDVERTEX);
          unuverts++;
        }
      }
    }
    // Return the dead tetrahedron to the pool of tetrahedra.
    tetrahedrondealloc(testtet.tet);
    virusloop = (tetrahedron **) viri->traverse();
  }
  
  delete [] tetspernodelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// assignregionattribs()    Assign each tetrahedron a region number.         //
//                                                                           //
// This routine is called when '-AA' switch is specified.  Every tetrahedron //
// of a (bounded) region will get a integer number to that region.  Default, //
// regions are numbered as 1, 2, 3, etc. However, if a number has already    //
// been used (set by user in the region section in .poly or .smesh), it is   //
// skipped and the next available number will be used.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::assignregionattribs()
{
  list *regionnumlist;
  list *regiontetlist;
  triface tetloop, regiontet, neightet;
  face checksh;
  bool flag;
  int regionnum, num;
  int attridx, count;
  int i;

  if (b->verbose > 0) {
    printf("  Assign region numbers.\n");
  }

  regionnumlist = new list(sizeof(int), NULL, 256);
  regiontetlist = new list(sizeof(triface), NULL, 1024);
  attridx = in->numberoftetrahedronattributes;  

  // Loop through all tets. Infect tets which already have a region number,
  //   and save the used numbers in 'regionnumlist'.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      regionnum = (int) elemattribute(tetloop.tet, attridx);
      if (regionnum != 0.0) {
        // Found a numbered region tet.
        infect(tetloop);
        regiontetlist->append(&tetloop);
        // Found and infect all tets in this region.
        for (i = 0; i < regiontetlist->len(); i++) {
          regiontet = * (triface *)(* regiontetlist)[i];
          for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) {
            // Is there a boundary face?
            tspivot(regiontet, checksh);
            if (checksh.sh == dummysh) {
              sym(regiontet, neightet);
              if ((neightet.tet != dummytet) && !infected(neightet)) {
#ifdef SELF_CHECK
                // neightet should have the same region number. Check it.
                num = (int) elemattribute(neightet.tet, attridx);
                assert(num == regionnum);
#endif
                infect(neightet);
                regiontetlist->append(&neightet);
              }
            }
          }
        }
        // Add regionnum to list if it is not exist.
        flag = false;
        for (i = 0; i < regionnumlist->len() && !flag; i++) {
          num = * (int *)(* regionnumlist)[i];
          flag = (num == regionnum);
        }
        if (!flag) regionnumlist->append(&regionnum);
        // Clear list for the next region.
        regiontetlist->clear();
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
  
  if (b->verbose > 0) {
    printf("  %d user-specified regions.\n", regionnumlist->len());
  }

  // Now loop the tets again. Assign region numbers to uninfected tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  regionnum = 1;  // Start region number.
  count = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      // An unassigned region tet.
      count++;
      do {
        flag = false;
        // Check if the region number has been used.
        for (i = 0; i < regionnumlist->len() && !flag; i++) {
          num = * (int *)(* regionnumlist)[i];
          flag = (num == regionnum);
        }
        if (flag) regionnum++;
      } while (flag);      
      setelemattribute(tetloop.tet, attridx, (REAL) regionnum);
      infect(tetloop);
      regiontetlist->append(&tetloop);
      // Found and infect all tets in this region.
      for (i = 0; i < regiontetlist->len(); i++) {
        regiontet = * (triface *)(* regiontetlist)[i];
        for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) {
          // Is there a boundary face?
          tspivot(regiontet, checksh);
          if (checksh.sh == dummysh) {
            sym(regiontet, neightet);
            if ((neightet.tet != dummytet) && !infected(neightet)) {
#ifdef SELF_CHECK
              // neightet should have not been assigned yet. Check it.
              num = (int) elemattribute(neightet.tet, attridx);
              assert(num == 0);
#endif
              setelemattribute(neightet.tet, attridx, (REAL) regionnum);
              infect(neightet);
              regiontetlist->append(&neightet);
            }
          }
        }
      }
      regiontetlist->clear();
      regionnum++; // The next region number.
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Uninfect all tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
#ifdef SELF_CHECK
    assert(infected(tetloop));
#endif
    uninfect(tetloop);
    tetloop.tet = tetrahedrontraverse();
  }
  
  if (b->verbose > 0) {
    printf("  %d regions are numbered.\n", count);
  }

  delete regionnumlist;
  delete regiontetlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carveholes()    Find the holes and infect them.  Find the volume          //
//                 constraints and infect them.  Infect the convex hull.     //
//                 Spread the infection and kill tetrahedra.  Spread the     //
//                 volume constraints.                                       //
//                                                                           //
// This routine mainly calls other routines to carry out all these functions.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carveholes()
{
  memorypool *holeviri, *regionviri;
  tetrahedron *tptr, **holetet, **regiontet;
  triface searchtet, *holetets, *regiontets;
  enum locateresult intersect;
  int i;

  if (!b->quiet) {
    printf("Removing unwanted tetrahedra.\n");
    if (b->verbose && (in->numberofholes > 0)) {
      printf("  Marking holes for elimination.\n");
    }
  }

  // Initialize a pool of viri to be used for holes, concavities.
  holeviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
  // Mark as infected any unprotected tetrahedra on the boundary.
  infecthull(holeviri);

  if (in->numberofholes > 0) {
    // Allocate storage for the tetrahedra in which hole points fall.
    holetets = (triface *) new triface[in->numberofholes];
    // Infect each tetrahedron in which a hole lies.
    for (i = 0; i < 3 * in->numberofholes; i += 3) {
      // Ignore holes that aren't within the bounds of the mesh.
      if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax)
          && (in->holelist[i + 1] >= ymin)
          && (in->holelist[i + 1] <= ymax)
          && (in->holelist[i + 2] >= zmin)
          && (in->holelist[i + 2] <= zmax)) {
        searchtet.tet = dummytet;
        // Find a tetrahedron that contains the hole.
        intersect = locate(&in->holelist[i], &searchtet);
        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
          // Record the tetrahedron for processing carve hole.
          holetets[i / 3] = searchtet;
        }
      }
    }
    // Infect the hole tetrahedron.  This is done by marking the tet as
    //   infected and including the tetrahedron in the virus pool.
    for (i = 0; i < in->numberofholes; i++) {
      infect(holetets[i]);
      holetet = (tetrahedron **) holeviri->alloc();
      *holetet = holetets[i].tet;
    }
    // Free up memory.
    delete [] holetets;
  }

  // Mark as infected all tets of the holes and concavities.
  plague(holeviri);
  // The virus pool contains all outside tets now.

  // Is -A switch in use.
  if (b->regionattrib) {
    // Assign every tetrahedron a regional attribute of zero.
    tetrahedrons->traversalinit();
    tptr = tetrahedrontraverse();
    while (tptr != (tetrahedron *) NULL) {
      setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0);
      tptr = tetrahedrontraverse();
    }
  }

  if (in->numberofregions > 0) {
    if (!b->quiet) {
      if (b->regionattrib) {
        if (b->varvolume) {
          printf("Spreading regional attributes and volume constraints.\n");
        } else {
          printf("Spreading regional attributes.\n");
        }
      } else {
        printf("Spreading regional volume constraints.\n");
      }
    }
    // Allocate storage for the tetrahedra in which region points fall.
    regiontets = (triface *) new triface[in->numberofregions];
    // Find the starting tetrahedron for each region.
    for (i = 0; i < in->numberofregions; i++) {
      regiontets[i].tet = dummytet;
      // Ignore region points that aren't within the bounds of the mesh.
      if ((in->regionlist[5 * i] >= xmin)
           && (in->regionlist[5 * i] <= xmax)
           && (in->regionlist[5 * i + 1] >= ymin)
           && (in->regionlist[5 * i + 1] <= ymax)
           && (in->regionlist[5 * i + 2] >= zmin)
           && (in->regionlist[5 * i + 2] <= zmax)) {
        searchtet.tet = dummytet;
        // Find a tetrahedron that contains the region point.
        intersect = locate(&in->regionlist[5 * i], &searchtet);
        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
          // Record the tetrahedron for processing after the
          //   holes have been carved.
          regiontets[i] = searchtet;
        }
      }
    }
    // Initialize a pool to be used for regional attrs, and/or regional
    //   volume constraints.
    regionviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
    // Find and set all regions.
    for (i = 0; i < in->numberofregions; i++) {
      if (regiontets[i].tet != dummytet) {
        // Make sure the tetrahedron under consideration still exists.
        //   It may have been eaten by the virus.
        if (!isdead(&(regiontets[i]))) {
          // Put one tetrahedron in the virus pool.
          infect(regiontets[i]);
          regiontet = (tetrahedron **) regionviri->alloc();
          *regiontet = regiontets[i].tet;
          // Apply one region's attribute and/or volume constraint.
          regionplague(regionviri, in->regionlist[5 * i + 3],
                       in->regionlist[5 * i + 4]);
          // The virus pool should be empty now.
        }
      }
    }
    // Free up memory.
    delete [] regiontets;
    delete regionviri;
  }

  // Now acutually remove the outside and hole tets.
  removeholetets(holeviri);
  // The mesh is nonconvex now.
  nonconvex = 1;

  if (b->regionattrib) {
    if (b->regionattrib > 1) {
      // -AA switch. Assign each tet a region number (> 0).
      assignregionattribs();
    }
    // Note the fact that each tetrahedron has an additional attribute.
    in->numberoftetrahedronattributes++;
  }

  // Free up memory.
  delete holeviri;
}

//
// End of carving out holes and concavities routines
//

//
// Begin of boundary Steiner points removing routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// replacepolygonsubs()    Substitute the subfaces of a polygon.             //
//                                                                           //
// 'oldshlist' (T_old) contains the old subfaces of P.  It will be replaced  //
// by 'newshlist' (T_new) of new subfaces. Each boundary edge of P is bonded //
// to 'dummysh' in T_new.                                                    //
//                                                                           //
// Notice that Not every boundary edge of T_new is able to bond to a subface,//
// e.g., when it is a segment recovered by removing a Steiner point in it.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist)
{
  face newsh, oldsh, spinsh;
  face casingout, casingin;
  face checkseg;
  point pa, pb;
  int i, j, k, l;

  for (i = 0; i < newshlist->len(); i++) {
    // Get a new subface s.
    newsh = * (face *)(* newshlist)[i];
    // Check the three edges of s.
    for (k = 0; k < 3; k++) {
      spivot(newsh, casingout);
      // Is it a boundary edge?
      if (casingout.sh == dummysh) {
        // Find the old subface s_o having the same edge as s.
        pa = sorg(newsh);
        pb = sdest(newsh); 
        for (j = 0; j < oldshlist->len(); j++) {
          oldsh = * (face *)(* oldshlist)[j];
	  for (l = 0; l < 3; l++) {
            if (((sorg(oldsh) == pa) && (sdest(oldsh) == pb)) ||
                ((sorg(oldsh) == pb) && (sdest(oldsh) == pa))) break;
            senextself(oldsh);
          }
          if (l < 3) break;
        }
        // Is there a matched edge?
        if (j < oldshlist->len()) {
          // Get the neighbor subface s_out.
          spivot(oldsh, casingout);
          sspivot(oldsh, checkseg);
          if (checkseg.sh != dummysh) {
            // A segment. Insert s into the face ring, ie, s_in -> s -> s_out.
            if (oldsh.sh != casingout.sh) {
              // s is not bonded to itself.
              spinsh = casingout;
              do {
                casingin = spinsh;
                spivotself(spinsh);
              } while (sapex(spinsh) != sapex(oldsh));
              assert(casingin.sh != oldsh.sh); 
              // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out).
              sbond1(casingin, newsh);
              sbond1(newsh, casingout);
            } else {
              // Bond newsh -> newsh.
              sbond(newsh, newsh);
            }
            // Bond the segment.
            ssbond(newsh, checkseg);
          } else {
            // Bond s <-> s_out (and dissolve s_out -> s_old).
            sbond(newsh, casingout);
          }
          // Unbound oldsh to indicate it's neighbor has been replaced.
          //   It will be used to indentfy the edge in the inverse.
          sdissolve(oldsh);
          ssdissolve(oldsh);
        }
      }
      // Go to the next edge of s.
      senextself(newsh);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// orientnewsubs()    Orient new subfaces facing to the inside of cavity.    //
//                                                                           //
// 'newshlist' contains new subfaces of the cavity C (created by re-triangu- //
// lation the polygon P). They're not necessary facing to the inside of C.   //
// 'orientsh', faces to the inside of C, is used to adjust new subfaces. The //
// normal of the new subfaces is returned in 'norm'.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm)
{
  face *newsh;
  point pa, pb, pc;
  REAL ref[3], ori, len;
  int i;

  // Calculate the normal of 'orientsh'.
  pa = sorg(*orientsh);
  pb = sdest(*orientsh);
  pc = sapex(*orientsh);
  facenormal(pa, pb, pc, norm, &len);
  for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i];
  for (i = 0; i < 3; i++) norm[i] /= len;
  
  // Orient new subfaces. Let the normal above each one.
  for (i = 0; i < newshlist->len(); i++) {
    newsh = (face *)(* newshlist)[i];
    pa = sorg(*newsh);
    pb = sdest(*newsh);
    pc = sapex(*newsh);
    ori = orient3d(pa, pb, pc, ref);
    assert(ori != 0.0);
    if (ori > 0.0) {
      sesymself(*newsh);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedflip()    Flip a non-constrained face.                         //
//                                                                           //
// 'flipface' f (abc) is a face we want to flip. In addition, if 'front' is  //
// given (not a NULL), f is a crossface. f may not be flippable if it is one //
// of the following cases:                                                   //
//   (1) f has an aux subface attached;                                      //
//   (2) f is on the convex hull;                                            //
//   (3) f is not locally Delaunay (f must be recovered by a previous flip,  //
//       we should keep it, otherwise, we may fall into a flip loop);        //
//   (4) f is T32 at ab, but abd or abe has an aux subface attached;         //
//   (5) f is T22 or T44 at ab, but abd, or abe, or abf has an aux subface   //
//       attached;                                                           //
//   (6) f is unflipable at ab, and abd, abe, ... are all unflippable due to //
//       the cases (1) - (5).                                                //
// If f is a crssface ('front' != NULL) and it is unflipable due to case (3),//
// (4), (5) and (6). Try to flip the next crossing face of front first.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::constrainedflip(triface* flipface, triface* front,
  queue* flipque)
{
  triface symface, spintet;
  face checksh;
  point pa, pb, pc, pd, pe;
  enum fliptype fc;
  REAL sign;
  bool doflip;
  int ia, ib, ic, id, ie;
  int i;

  // (1) Is f protected by an (auxilary) subface?
  tspivot(*flipface, checksh);
  if (checksh.sh != dummysh) return false;
  // (2) Is f on the convex hull?
  sym(*flipface, symface);
  if (symface.tet == dummytet) return false;
  // (3) Is f not locally Delaunay?
  adjustedgering(*flipface, CCW);
  pa = dest(*flipface);
  pb = org(*flipface);
  pc = apex(*flipface);
  pd = oppo(*flipface);
  pe = oppo(symface);
  // if (symbolic) {
    ia = pointmark(pa);
    ib = pointmark(pb);
    ic = pointmark(pc);
    id = pointmark(pd);
    ie = pointmark(pe);
    sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie);
    assert(sign != 0.0);
  // } else {
  //   sign = insphere(pa, pb, pc, pd, pe);
  // }
  if (sign <= 0.0) {
    // Get the fliptype of f.
    checksubfaces = 0; // switch off subface test.
    fc = categorizeface(*flipface);
    checksubfaces = 1; // switch on subface test.
    if (fc == T23) {
      doflip = true;
      // Avoid one tet created by the 2-3 flip is nearly degenerate.
      /* pc = oppo(*flipface);
      pd = oppo(symface);
      adjustedgering(*flipface, CCW);
      for (i = 0; i < 3; i++) {
        pa = org(*flipface);
        pb = dest(*flipface);
        ori = orient3d(pa, pb, pc, pd);
        if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) {
          doflip = false; break;
        }
        enextself(*flipface);
      } */
      if (doflip) {
        flip23(flipface, flipque);
        return true;
      }
    } else if (fc == T32) {
      // (4) Is abd, or abe protected?
      doflip = true;
      spintet = *flipface;
      for (i = 0; i < 2; i++) {
        fnextself(spintet);
        tspivot(spintet, checksh);
        if (checksh.sh != dummysh) {
          doflip = false; break; // f is protected. Unflipable.
        }
      }
      if (doflip) {
        flip32(flipface, flipque);
        return true;
      }
    } else if (fc == T22 || fc == T44) {
      // (5) Is abd, abe, or abf protected?
      doflip = true;
      if (fc == T22) {
        for (i = 0; i < 2; i++) {
          spintet = *flipface;
          if (i == 1) {
            esymself(spintet);
          }
          fnextself(spintet);
          tspivot(spintet, checksh);
          if (checksh.sh != dummysh) {
            doflip = false; break; // f is protected. Unflipable.
          }
        }
      } else if (fc == T44) {
        spintet = *flipface;
        for (i = 0; i < 3; i++) {
          fnextself(spintet);
          tspivot(spintet, checksh);
          if (checksh.sh != dummysh) {
            doflip = false; break; // f is protected. Unflipable.
          }
        }
      }
      if (doflip) {
        flip22(flipface, flipque);
        return true;
      }
    } else if (fc == N32) {
      // Is f a crossface?
      if (front != (triface *) NULL) {
        // (6) Is any obstacle face (abd, or abe, ...) flipable?
        spintet = *flipface;
        while (fnextself(spintet)) {
          if (apex(spintet) == apex(*flipface)) break;
          // Check if spintet is flipable, no recursive.
          if (constrainedflip(&spintet, NULL, flipque)) {
            // One obstacle face has been flipped.
            return true;
          }
          // Unflipable. Go to the next obstacle face.
          findedge(&spintet, org(*flipface), dest(*flipface));
        }
      }
    }
  }

  // f is unflipable. Is f a crossface?
  if (front != (triface *) NULL) {
    // Look if there is another crossface.
    pa = org(*front);
    pb = dest(*front);
    pc = apex(*front);
    // sym(*flipface, symface);
    // Have we reach the end of abc (We've started from edge ab).
    if (oppo(symface) != pc) {
      adjustedgering(symface, CCW);
      for (i = 0; i < 3; i++) {
        fnext(symface, spintet);
        // Is c ahead of this face?
        sign = orient3d(org(spintet), dest(spintet), apex(spintet), pc);
        if (sign < 0.0) {
          if (tritritest(&spintet, pa, pb, pc)) {
            if (b->verbose > 2) {
              printf("    Next crossface (%d, %d, %d).\n",
                     pointmark(org(spintet)), pointmark(dest(spintet)),
                     pointmark(apex(spintet)));
            }
            return constrainedflip(&spintet, front, flipque);
            // return constrainedflip(&spintet, NULL, flipque);
          }
        }
        enextself(symface);
      }
    }
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoverfront()    Recover a missing front by flips.                       //
//                                                                           //
// 'front' f is missing in D - it was crossed by faces of D. The cross faces //
// may be flippable, so f can be recovered by flipping them away.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::recoverfront(triface* front, list* newtetlist, queue* flipque)
{
  triface idfront, starttet, spintet;
  point pa, pb, pc, pd, ref;
  enum locateresult loc;
  enum finddirectionresult col;
  REAL ori, ori1, ori2, sign;
  int hitbdry;
  int i, j;

  // Find an existing edge of f in D to start with.
  for (i = 0; i < 3; i++) {
    pa = org(*front);
    pb = dest(*front);
    // Get a tet for searching.
    idfront = recenttet;
    // Make sure the tet is valid (flip32() may kill a tet).
    if (isdead(&idfront)) {
      // The tet is dead. Get a live tet in D. !!!
      for (j = 0; j < newtetlist->len(); j++) {
        recenttet = * (triface *)(* newtetlist)[j];
        if (!isdead(&recenttet)) break;
      }
      assert(j < newtetlist->len());
    }
    loc = preciselocate(pa, &idfront, (long) newtetlist->len());
    if (loc != ONVERTEX) {
      // Do a brute-force search in D.
      for (j = 0; j < newtetlist->len(); j++) {
        idfront = * (triface *)(* newtetlist)[j];
        if (isdead(&idfront)) continue;
        if (findorg(&idfront, pa)) break;
      }
      assert(j < newtetlist->len()); // a must belong to one tet.
    }
    recenttet = idfront;
    // Search for a tet having edge ab.
    col = finddirection(&idfront, pb, (long) newtetlist->len());
    if (col == BELOWHULL) {
      // Do a brute-force search in D.
      for (j = 0; j < newtetlist->len(); j++) {
        idfront = * (triface *)(* newtetlist)[j];
        if (isdead(&idfront)) continue;
        if (findorg(&idfront, pa)) {
          assert(org(idfront) == pa);
          if (dest(idfront) == pb) {
            col = RIGHTCOLLINEAR; break;
          } else if (apex(idfront) == pb) {
            col = LEFTCOLLINEAR; break;
          } else if (oppo(idfront) == pb) {
            col = TOPCOLLINEAR; break;
          }
        }
      }
    }
    if (col == RIGHTCOLLINEAR) {
      // b is just the destination.
    } else if (col == LEFTCOLLINEAR) {
      enext2self(idfront);
      esymself(idfront);
    } else if (col == TOPCOLLINEAR) {
      fnextself(idfront);
      enext2self(idfront);
      esymself(idfront);
    }
    if (dest(idfront) == pb) break; // Found.
    // Missing. Go to the next edge of f.
    enextself(*front);
  }
  if (i == 3) {
    // All three edges of f are missing - unrecoverable.
    return false;
  }

  // Search for a tet having f (abc).
  pc = apex(*front);
  spintet = idfront;
  hitbdry = 0;
  do {
    if (apex(spintet) == pc) {
      // Found abc. Insert an auxilary subface s at idfront.
      insertauxsubface(front, &spintet);
      return true;
    }
    if (!fnextself(spintet)) {
      hitbdry ++;
      if (hitbdry < 2) {
        esym(idfront, spintet);
        if (!fnextself(spintet)) {
          hitbdry ++;
        }
      }
    }
    if (apex(spintet) == apex(idfront)) break;
  } while (hitbdry < 2);

  // Search for a crossing face to flip.
  pd = apex(idfront);
  assert(pd != pc);
  // Decide the orientation of d with abc.
  ori = orient3d(pa, pb, pc, pd);
  if (ori < 0.0) {
    // d is above abc. Rotate downwards.
    esym(idfront, starttet);
    sign = -1.0;
  } else if (ori > 0.0) {
    // d is below abc. Rotate upwards.
    starttet = idfront;
    sign = 1.0;
  } else {
    assert(ori == 0.0);
    // d is coplanar with abc. Do abc and abd intersect?
    ref = oppo(idfront);
    ori1 = orient3d(pa, pb, ref, pc);
    ori2 = orient3d(pa, pb, ref, pd);
    assert(ori1 * ori2 != 0.0);
    if (ori1 * ori2 > 0) {
      // abc and abd intersect.  There're two possible intersections: 
      //   ad and bc, or ac and bd.  Find it out.
      ori1 = orient3d(pb, pc, ref, pd);
      ori2 = orient3d(pb, pc, ref, pa);
      assert(ori1 * ori2 != 0.0);
      if (ori1 * ori2 > 0) {
        // ac intersects bd.
        enextself(idfront); // go to edge bd.
      } else {
        // ad intersects bc.
        enext2self(idfront); // go to edge ad.
      }
      adjustedgering(idfront, CCW);
      fnextself(idfront); // face ade or bce need a 4-to-4 flip.
      if (b->verbose > 2) {
        printf("    Get crossface (%d, %d, %d).\n", pointmark(org(idfront)),
               pointmark(dest(idfront)), pointmark(apex(idfront)));
      }
      if (constrainedflip(&idfront, front, flipque)) {
        // A crossface has been flipped. Continue to recover f.
        return recoverfront(front, newtetlist, flipque);
      }
      // Unable to recover f.
      return false; // sign = 0.0;
    } else {
      // Not intersect. We can go either direction.
      starttet = idfront;
      if (fnextself(starttet)) {
        // Choose to rotate upwards.
        sign = 1.0;
      } else {
        // Hit convex hull. Choose to rotate downwrads.
        esym(idfront, starttet);
        sign = -1.0;
      }
    }
  }

  assert(sign != 0.0);
  if (sign == -1) {
    // The edge ab has be changed. Reverse it.
    pa = org(starttet);
    pb = dest(starttet);
    // The sign has been reversed as well.
    sign = -sign;
  }
  // Rotate face abd around edge ab. Moreover, we've chosen the rotate
  //   direction such that no convex hull face will be reach.
  spintet = starttet;
  while (fnextself(spintet)) {
    pd = apex(spintet);
    assert(pd != pc);
    // Check if the orientation of d (with abc) has changed.
    ori = orient3d(pa, pb, pc, pd);
    if (ori == 0.0) {
      // abc and abd must coplanar intersect (4-to-4 flip is needed).
      ref = oppo(spintet);
      ori1 = orient3d(pb, pc, ref, pd);
      ori2 = orient3d(pb, pc, ref, pa);
      assert(ori1 * ori2 != 0.0);
      if (ori1 * ori2 > 0) {
        // ac intersects bd.
        enextself(spintet); // go to edge bd.
      } else {
        // ad intersects bc.
        enext2self(spintet); // go to edge ad.
      }
      adjustedgering(spintet, CCW);
      fnextself(spintet); // face ade or bce need a 4-to-4 flip.
      if (b->verbose > 2) {
        printf("    Get crossface (%d, %d, %d).\n", pointmark(org(spintet)),
               pointmark(dest(spintet)), pointmark(apex(spintet)));
      }
      if (constrainedflip(&spintet, front, flipque)) {
        // A crossface has been flipped. Continue to recover f.
        return recoverfront(front, newtetlist, flipque);
      }
      // Unable to recover f.
      return false; // sign = 0.0;
    } else if (ori * sign < 0.0) {
      // Sign has changed. The face dea or deb must cross abc.
      adjustedgering(spintet, CCW);
      enextself(spintet);
      for (i = 0; i < 2; i++) {
        // Get the face dea or deb.
        fnext(spintet, starttet);
        if (tritritest(&starttet, pa, pb, pc)) {
          if (b->verbose > 2) {
            printf("    Get crossface (%d, %d, %d).\n",
                   pointmark(org(starttet)), pointmark(dest(starttet)),
                   pointmark(apex(starttet)));
          }
          if (constrainedflip(&starttet, front, flipque)) {
            // A crossface has been flipped. Continue to recover f.
            return recoverfront(front, newtetlist, flipque);
          }
        }
        enextself(spintet);
      }
      // Unable to recover f.
      return false;
    }
  }
  // Impossible to be here.
  assert(0);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairflips()    Flip non-Delaunay and non-constrained faces.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairflips(queue* flipque)
{
  badface *qface;
  triface flipface, symface, spintet;
  face checksh;
  point pa, pb, pc, pd, pe;
  enum fliptype fc;
  REAL sign;
  long flipcount;
  bool doflip;
  int ia, ib, ic, id, ie;
  int i;

  if (b->verbose > 1) {
    printf("    Repair flip %ld faces.\n", flipque->len());
  }
  flipcount = flip23s + flip32s + flip22s + flip44s;
  // Loop until the queue is empty.
  while (!flipque->empty()) {
    qface = (badface *) flipque->pop();
    flipface = qface->tt;
    // Check the validity of this face.
    if (isdead(&flipface) || flipface.tet == dummytet || 
        (org(flipface) != qface->forg) || 
        (dest(flipface) != qface->fdest) ||
        (apex(flipface) != qface->fapex) ||
        (oppo(flipface) == (point) NULL)) continue;
    // (1) Is f protected by an (auxilary) subface?
    tspivot(flipface, checksh);
    if (checksh.sh != dummysh) continue;
    // (2) Is f on the convex hull?
    sym(flipface, symface);
    if (symface.tet == dummytet) continue;
    // For positive orientation that insphere() test requires.
    adjustedgering(flipface, CW);
    pa = org(flipface);
    pb = dest(flipface);
    pc = apex(flipface);
    pd = oppo(flipface);
    pe = oppo(symface);
    // if (symbolic) {
      ia = pointmark(pa);
      ib = pointmark(pb);
      ic = pointmark(pc);
      id = pointmark(pd);
      ie = pointmark(pe);
      sign = insphere_sos(pa, pb, pc, pd, pe, ia, ib, ic, id, ie);
      assert(sign != 0.0);
    // } else {
    //   sign = insphere(pa, pb, pc, pd, pe);
    // }
    if (sign > 0.0) {
      // f is non-lcally Delaunay. Get the fliptype of f.
      checksubfaces = 0; // switch off subface test.
      fc = categorizeface(flipface);
      checksubfaces = 1; // switch on subface test.
      if (fc == T23) {
        doflip = true;
        // Avoid to create a nearly degenerate tet.
        /* pc = oppo(flipface);
        pd = oppo(symface);
        adjustedgering(flipface, CCW);
        for (i = 0; i < 3; i++) {
          pa = org(flipface);
          pb = dest(flipface);
          ori = orient3d(pa, pb, pc, pd);
          if (iscoplanar(pa, pb, pc, pd, ori, b->epsilon)) {
            doflip = false; break;
          }
          enextself(flipface);
        } */
        if (doflip) {
          flip23(&flipface, flipque);
        }
      } else if (fc == T32) {
        // (4) Is abd, or abe protected?
        doflip = true;
        spintet = flipface;
        for (i = 0; i < 2; i++) {
          fnextself(spintet);
          tspivot(spintet, checksh);
          if (checksh.sh != dummysh) {
            doflip = false; break; // f is protected. Unflipable.
          }
        }
        if (doflip) {
          flip32(&flipface, flipque);
        }
      } else if (fc == T22 || fc == T44) {
        // (5) Is abd, abe, or abf protected?
        doflip = true;
        if (fc == T22) {
          for (i = 0; i < 2; i++) {
            spintet = flipface;
            if (i == 1) {
              esymself(spintet);
            }
            fnextself(spintet);
            tspivot(spintet, checksh);
            if (checksh.sh != dummysh) {
              doflip = false; break; // f is protected. Unflipable.
            }
          }
        } else if (fc == T44) {
          spintet = flipface;
          for (i = 0; i < 3; i++) {
            fnextself(spintet);
            tspivot(spintet, checksh);
            if (checksh.sh != dummysh) {
              doflip = false; break; // f is protected. Unflipable.
            }
          }
        }
        if (doflip) {
          flip22(&flipface, flipque);
        }
      }
    }
  }
  flipcount = flip23s + flip32s + flip22s + flip44s - flipcount;
  if (b->verbose > 1) {
    printf("    %ld flips.\n", flipcount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedcavity()    Tetrahedralize a cavity by constrained tetrahedra. //
//                                                                           //
// The cavity C is bounded by faces F in 'floorlist' and 'ceillist'. 'ptlist'//
// V is the set of vertices of C.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist,
  list* ceillist, list* ptlist, list* frontlist, list* misfrontlist,
  list* newtetlist, queue* flipque)
{
  triface misfront, newtet;
  long facenum;
  int i;

  if (b->verbose > 1) {
    printf("    Constrained cavity (%d floors, %d ceilings, %d vertices).\n",
           floorlist->len(), ceillist->len(), ptlist->len());
  }

  // symbolic = 1;
  
  // Initialize the cavity C.
  initializecavity(floorlist, ceillist, frontlist);
  // Form the D of the vertices of C.
  delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque);
  
  // Identify faces of C in D.
  if (!identifyfronts(frontlist, misfrontlist, newtetlist)) {
    // Some faces are missing.
    recenttet = * (triface *)(* newtetlist)[0];
    assert((recenttet.tet != dummytet) && !isdead(&recenttet));
    // Try to recover missing faces by flips.
    do {
      facenum = misfrontlist->len();
      for (i = 0; i < misfrontlist->len(); i++) {
        // Get a missing front f.
        misfront = * (triface *)(* misfrontlist)[i];
        // Let f face toward the inside of C.
        adjustedgering(misfront, CW);
        if (b->verbose > 1) {
          printf("    Recover face (%d, %d, %d).\n", pointmark(org(misfront)),
                 pointmark(dest(misfront)), pointmark(apex(misfront)));
        }
        if (recoverfront(&misfront, newtetlist, flipque)) {
          // f has been recovered.
          frontlist->append(&misfront);
          misfrontlist->del(i, 0); i--;
        }
        // Flip non-locally non-constrained Delaunay faces.
        repairflips(flipque);
      }
      // Have all faces been recovered?
      if (misfrontlist->len() == 0) break;
      // No! There are still un-recovered faces. Continue the loop if any
      //   face has been recovered.
    } while (misfrontlist->len() < facenum);
    // Retrieve new tets and purge dead tets in D.
    retrievenewtets(newtetlist);
  }
  
  // symbolic = 0;

  if (misfrontlist->len() == 0) {
    // All fronts have identified in D. Get the shape of C by removing out
    //   tets of C. 'misfrontlist' is reused for removing out tets.
    //   Don't do flip since the new tets may get deleted later.
    carvecavity(newtetlist, misfrontlist, NULL);
    // Recover locally Delaunay faces.
    // flip(flipque, NULL);
    return true;
  } else {
    // Fail to tetrahedralize C.
    // Remove aux subfaces.
    detachauxsubfaces(newtetlist);
    // Remove new tets.
    for (i = 0; i < newtetlist->len(); i++) {
      newtet = * (triface *)(* newtetlist)[i];
      assert(!isdead(&newtet));
      tetrahedrondealloc(newtet.tet);
    }
    newtetlist->clear();
    // Restore faces of C in frontlist.
    for (i = 0; i < misfrontlist->len(); i++) {
      misfront = * (triface *)(* misfrontlist)[i];
      frontlist->append(&misfront);
    }
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// expandsteinercavity()    Expand the cavity of a Steiner point.            //
//                                                                           //
// Expand the cavity C if there fronts (except fronts having subfaces) which //
// are either (nearly) coplanar or invisible by the Steiner point.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::expandsteinercavity(point steinpt, REAL eps, list* frontlist,
  list* oldtetlist)
{
  triface front, symfront, newfront, oldfront;
  face frontsh;
  point pa, pb, pc;
  REAL ori;
  bool expflag, newflag;
  int i, j;

  do {
    expflag = false;
    for (i = 0; i < frontlist->len(); i++) {
      // Get a front f.
      front =  * (triface *)(* frontlist)[i];
      // f can be expanded if it is not a subface.
      tspivot(front, frontsh);
      if (frontsh.sh == dummysh) {
        // Let f face to the inside of C.
        adjustedgering(front, CW);
        pa = org(front);
        pb = dest(front);
        pc = apex(front);
        ori = orient3d(pa, pb, pc, steinpt);
        if (ori != 0.0) {
          if (iscoplanar(pa, pb, pc, steinpt, ori, eps)) {
            ori = 0.0; // f is nearly coplanar with p.
          }
        }
        if (ori >= 0.0) {
          // f is either invisible or coplanar with p.
          if (b->verbose > 2) {
            printf("    Remove front (%d, %d, %d).\n", pointmark(pa),
                   pointmark(pb), pointmark(pc));
          }
          frontlist->del(i, 1);
          expflag = true;
          break;
        }
      }
    }
    if (expflag) {
      assert(!infected(front) && (oppo(front) != NULL));
      // Expand C at f by including new fronts.
      adjustedgering(front, CCW);
      for (i = 0; i < 3; i++) {
        newflag = true;
        // Get a new boundary n of the cavity.
        fnext(front, symfront);
        tspivot(symfront, frontsh);
        sym(symfront, newfront);
        if (frontsh.sh == dummysh) {
          assert(newfront.tet != dummytet);
          // Is n a front of the unexp. cavity?
          if (infected(newfront)) {
            for (j = 0; j < frontlist->len(); j++) {
              oldfront = * (triface *)(* frontlist)[j];
              if ((oldfront.tet == symfront.tet) &&
                  (oldfront.loc == symfront.loc)) {
                // n is not a front anymore.
                if (b->verbose > 2) {
                  printf("    Remove front (%d, %d, %d).\n",
                         pointmark(org(oldfront)), pointmark(dest(oldfront)),
                         pointmark(apex(oldfront)));
                }
                frontlist->del(j, 1);
                newflag = false;
                break;
              }
            }
          }
        } else {
          // n is a subface.
          if (newfront.tet == dummytet) {
            sesymself(frontsh);
            // Create a fake tet to hold n.
            maketetrahedron(&newfront);
            setorg(newfront, sorg(frontsh));
            setdest(newfront, sdest(frontsh));
            setapex(newfront, sapex(frontsh));
            setoppo(newfront, (point) NULL);
            tsbond(newfront, frontsh);
          } else {
            // n should not be a front of cavity yet.
            assert(!infected(newfront));
          }
        }
        if (newflag) {
          if (b->verbose > 2) {
            printf("    Add front (%d, %d, %d).\n", pointmark(org(newfront)),
                   pointmark(dest(newfront)), pointmark(apex(newfront)));
          }
          frontlist->append(&newfront);
        }
        enextself(front);
      }
      // Add f into oldtetlist (to be deleted).
      infect(front);
      oldtetlist->append(&front);
      expcavcount++;
    }
  } while (expflag);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findrelocatepoint()    Find new location for relocating a point.          //
//                                                                           //
// 'frontlist' contains the boundary faces of the cavity C.  Some fronts are //
// visible by 'stpt' p, some are coplanar with p.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findrelocatepoint(point sp, point np, REAL* n,
  list* frontlist, list* oldtetlist)
{
  triface front;
  point pa, pb, pc;
  REAL tp[3], tvol, mvol;
  REAL ori, eps;
  bool visible;
  int i, j, k;

  if (b->verbose > 1) {
    printf("    Find new location for point %d.\n", pointmark(sp));
  }

  // Avoid compilation warnings.
  tvol = mvol = 0.0;
  visible = false;

  eps = b->epsilon;
  // Initialize np far enough from p (outside C).
  for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i];
  // Let tp = np;
  for (i = 0; i < 3; i++) tp[i] = np[i];
  // Interation to adjust np until it is visible by all fronts.
  j = 0;
  do {
    for (i = 0; i < frontlist->len(); i++) {
      // Get a front face f.
      front = * (triface *)(* frontlist)[i];
      // Let f face to the interior of C.
      adjustedgering(front, CW);
      pa = org(front);
      pb = dest(front);
      pc = apex(front);
      ori = orient3d(pa, pb, pc, np);
      visible = (ori < 0.0);
      if (!visible) {
        // A front is invisible by np. Move it towards p along the normal.
        for (i = 0; i < 3; i++) np[i] = sp[i] + 0.5 * (sp[i] - np[i]);
        // Failed if tp = np.
        if ((tp[0] == np[0]) && (tp[1] == np[1]) && (tp[2] == np[2])) {
          // Try to expand the cavity.
          expandsteinercavity(sp, eps, frontlist, oldtetlist);
          eps *= 10.0;
          if (eps > b->epsilon * 1000.0) {
          // printf("Internal error: Fail to relocate pt %d.\n",pointmark(sp));
            // internalerror();
            return false;
          }
          // Restart the point relocation.
          for (i = 0; i < 3; i++) np[i] = sp[i] + longest * n[i];
        }
        if (j % 2) {
          // Set tp = np (at every 2 steps) to catch the stop state.
          for (i = 0; i < 3; i++) tp[i] = np[i];
        }
        break;
      } else {
        // Save the smallest volume.
        if (i == 0) {
          mvol = fabs(ori);
        } else {
          mvol = fabs(ori) < mvol ? fabs(ori) : mvol;
        }
      }
    }
    j++;
  } while (!visible);
  
  if (b->verbose > 1) {
    printf("    %d iterations. minvol = %.12g.\n", j, mvol);
  }

  // Continue to adjust np until the minimal volume of tets formed by
  //   fronts and np doesn't increase (all fronts are visible by np).
  k = 0;
  do {
    j = 0;
    do {
      if (k == 0) {
        // Initial tp := np + 0.9 * (p - np). Move toward p.
        for (i = 0; i < 3; i++) tp[i] = sp[i] + 0.9 * (np[i] - sp[i]);
      } else {
        // Initial tp := np + 1.1 * (p - np). Move away from p.
        for (i = 0; i < 3; i++) tp[i] = sp[i] + 1.1 * (np[i] - sp[i]);
      }
      // Get the minial volume formed by tp with one of the fronts.
      for (i = 0; i < frontlist->len(); i++) {
        // Get a front face f.
        front = * (triface *)(* frontlist)[i];
        // Let f face to the interior of C.
        adjustedgering(front, CW);
        pa = org(front);
        pb = dest(front);
        pc = apex(front);
        ori = orient3d(pa, pb, pc, tp);
        visible = (ori < 0.0);
        if (visible) {
          // Save the smallest volume.
          if (i == 0) {
            tvol = fabs(ori);
          } else {
            tvol = fabs(ori) < tvol ? fabs(ori) : tvol;
          }
        } else {
          // A front is invisible by tp. Stop.
          tvol = 0.0;
          break;
        }
      }
      if (tvol > mvol) {
        // Get a larger minimal volume.
        for (i = 0; i < 3; i++) np[i] = tp[i];
        mvol = tvol;
      } else {
        // Minimal volume decreases. Stop.
        break;
      }
      // Continue to adjust np.
      j++;
    } while (true);
    // Has np been adjusted?
    if (j > 0) break;
    // Try to move np to anoter direction.
    k++;
  } while (k < 2);

  if (b->verbose > 1) {
    printf("    %d adjust iterations. minvol = %.12g.\n", j, mvol);
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// relocatepoint()    Relocate a point into the cavity.                      //
//                                                                           //
// 'frontlist' contains the boundary faces of the cavity C. All fronts must  //
// be visible by 'steinpt'.  Some fronts may hold by 'fake' tets (they are   //
// hull faces).  Fake tets will be removed when they're finished.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist,
  list* newtetlist, queue* flipque)
{
  triface front, newtet, newface, neightet;
  face checksh;
  point pa, pb;
  REAL attrib, volume;
  bool bdflag;
  int i, j, k, l;

  if (b->verbose > 1) {
    printf("    Insert Steiner point (%.12g, %.12g, %.12g) %d.\n",
           steinpt[0], steinpt[1], steinpt[2], pointmark(steinpt));
  }
  // Clear the list first.
  newtetlist->clear();

  // Create the tets formed by fronts and 'steinpt'.
  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)(* frontlist)[i];
    // Let f face inside C. (f is a face of tet adjacent to C).
    adjustedgering(front, CW);
    if (b->verbose > 2) {
      printf("    Get front (%d, %d, %d).\n", pointmark(org(front)),
             pointmark(dest(front)), pointmark(apex(front)));
    }
    maketetrahedron(&newtet);
    newtetlist->append(&newtet);
    setorg(newtet, org(front));
    setdest(newtet, dest(front));
    setapex(newtet, apex(front));
    setoppo(newtet, steinpt);
    if (oldtet != (triface *) NULL) {
      for (j = 0; j < in->numberoftetrahedronattributes; j++) {
        attrib = elemattribute(oldtet->tet, j);
        setelemattribute(newtet.tet, j, attrib);
      }
      if (b->varvolume) {
        volume = volumebound(oldtet->tet);
        setvolumebound(newtet.tet, volume);
      }
    }
    // 'front' may be a 'fake' tet.
    tspivot(front, checksh);
    if (oppo(front) == (point) NULL) {
      if (checksh.sh != dummysh) {
        stdissolve(checksh);
      }
      // Dealloc the 'fake' tet.
      tetrahedrondealloc(front.tet);
      // This side (newtet) is a boundary face, let 'dummytet' bond to it.
      //   Otherwise, 'dummytet' may point to a dead tetrahedron after the
      //   old cavity tets are removed.
      dummytet[0] = encode(newtet);
    } else {
      // Bond two tetrahedra, also dissolve the old bond at 'front'.
      bond(newtet, front);
    }
    if (checksh.sh != dummysh) {
      sesymself(checksh);
      tsbond(newtet, checksh);
    }
    if (flipque != (queue *) NULL) {
      // f may be non-locally Delaunay and flipable.
      enqueueflipface(newtet, flipque);
    }
    // The three neighbors are open. Will be finished later.
  }

  // Connect new tets in C. All connecting faces must contain 'steinpt'.
  for (i = 0; i < newtetlist->len(); i++) {
    newtet = * (triface *)(* newtetlist)[i];
    newtet.ver = 0;
    for (j = 0; j < 3; j++) {
      fnext(newtet, newface);
      sym(newface, neightet);
      if (neightet.tet == dummytet) {
        // Find a neightet to connect it.
        bdflag = false;
        pa = org(newface);
        pb = dest(newface);
        assert(apex(newface) == steinpt);
        for (k = i + 1; k < newtetlist->len() && !bdflag; k++) {
          neightet = * (triface *)(* newtetlist)[k];
          neightet.ver = 0;
          for (l = 0; l < 3; l++) {
            if ((org(neightet) == pa && dest(neightet) == pb) ||
                (org(neightet) == pb && dest(neightet) == pa)) {
              // Find the neighbor.
              fnextself(neightet);
              assert(apex(neightet) == steinpt);
              // Now neightet is a face same as newface, bond them.
              bond(newface, neightet);
              bdflag = true;
              break;
            }
            enextself(neightet);
          }
        }
        assert(bdflag);
      }
      enextself(newtet);
    }
    // Let the corners of newtet point to it for fast searching.
    pa = org(newtet);
    setpoint2tet(pa, encode(newtet));
    pa = dest(newtet);
    setpoint2tet(pa, encode(newtet));
    pa = apex(newtet);
    setpoint2tet(pa, encode(newtet));
    pa = oppo(newtet);
    setpoint2tet(pa, encode(newtet));
  }

  if (flipque != (queue *) NULL) { 
    // Recover locally Delaunay faces.
    flip(flipque, NULL);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findcollapseedge()    Find collapseable edge to suppress an endpoint.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findcollapseedge(point suppt, point *conpt, list* oldtetlist,
  list* ptlist)
{
  triface front;
  point pt, pa, pb, pc;
  REAL *lenarray, ltmp, ori;
  bool visflag;
  int *idxarray, itmp;
  int n, i, j;

  if (b->verbose > 2) {
    printf("    Search an edge (in %d edges) for collapse %d.\n",
           ptlist->len(), pointmark(suppt));
  }

  // Candidate edges are p to the points of B(p) (in 'ptlist').
  n = ptlist->len();
  lenarray = new REAL[n];
  idxarray = new int[n];
  // Sort the points of B(p) by distance to p.
  for (i = 0; i < n; i++) {
    pt = * (point *)(* ptlist)[i];
    lenarray[i] = distance(suppt, pt);
    idxarray[i] = i;
  }
  // Bubble sort.
  for (i = 0; i < n - 1; i++) {
    for (j = 0; j < n - 1 - i; j++) {
      if (lenarray[j + 1] < lenarray[j]) {  // compare the two neighbors
        ltmp = lenarray[j];           // swap a[j] and a[j + 1]
        lenarray[j] = lenarray[j + 1];
        lenarray[j + 1] = ltmp;
        itmp = idxarray[j];           // swap a[j] and a[j + 1]
        idxarray[j] = idxarray[j + 1];
        idxarray[j + 1] = itmp;
      }
    }
  }
  // For each point q of B(p), test if the edge (p, q) can be collapseed.
  for (i = 0; i < n; i++) {
    pt = * (point *)(* ptlist)[idxarray[i]];
    // Is q visible by faces of B(p) not with q as a vertex.
    lenarray[i] = 0.0; // zero volume.
    visflag = true;
    for (j = 0; j < oldtetlist->len() && visflag; j++) {
      front = * (triface *)(* oldtetlist)[j];
      // Let f face to inside of B(p).
      adjustedgering(front, CCW);
      pa = org(front);
      pb = dest(front);
      pc = apex(front);
      // Is f contains q?
      if ((pa != pt) && (pb != pt) && (pc != pt)) {
        ori = orient3d(pa, pb, pc, pt);
        if (ori != 0.0) {
          if (iscoplanar(pa, pb, pc, pt, ori, b->epsilon * 1e+2)) ori = 0.0;
        }
        visflag = ori < 0.0;
        if (visflag) {
          // Visible, set the smallest volume.
          if (j == 0) {
            lenarray[i] = fabs(ori);
          } else {
            lenarray[i] = fabs(ori) < lenarray[i] ? fabs(ori) : lenarray[i];
          }
        } else {
          // Invisible. Do not collapse (p, q).
          lenarray[i] = 0.0;
        }
      }
    }
    if ((b->verbose > 2) && visflag) {
      printf("    Got candidate %d vol(%g).\n", pointmark(pt), lenarray[i]);
    }
  }

  // Select the largest non-zero volume (result in ltmp).
  ltmp = lenarray[0];
  itmp = idxarray[0];
  for (i = 1; i < n; i++) {
    if (lenarray[i] != 0.0) {
      if (lenarray[i] > ltmp) {
        ltmp = lenarray[i];
        itmp = idxarray[i]; // The index to find the point.
      }
    }
  }

  delete [] lenarray;
  delete [] idxarray;

  if (ltmp == 0.0) {
    // No edge can be collapseed.
    *conpt = (point) NULL;
    return false;
  } else {
    pt = * (point *)(* ptlist)[itmp];
    *conpt = pt;
    return true;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// collapseedge()    Remove a point by edge collapse.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::collapseedge(point suppt, point conpt, list* oldtetlist,
  list* deadtetlist)
{
  triface oldtet, deadtet;
  triface adjtet1, adjtet2;
  face adjsh;
  point pa, pb, pc;
  int i, j;

  if (b->verbose > 2) {
    printf("    Collapse edge (%d,%d).\n", pointmark(suppt), pointmark(conpt));
  }

  // Loop in B(p), replace p with np, queue dead tets, uninfect old tets.
  for (i = 0; i < oldtetlist->len(); i++) {
    oldtet = * (triface *)(* oldtetlist)[i]; // assert(infected(oldtet));
    uninfect(oldtet);
    pa = org(oldtet);
    pb = dest(oldtet);
    pc = apex(oldtet);
    assert(oppo(oldtet) == suppt);
    setoppo(oldtet, conpt);
    if ((pa == conpt) || (pb == conpt) || (pc == conpt)) {
      deadtetlist->append(&oldtet); // a collpased tet.
    }
  }
  // Loop in deadtetlist, glue adjacent tets of dead tets.
  for (i = 0; i < deadtetlist->len(); i++) {
    deadtet = * (triface *)(* deadtetlist)[i];
    // Get the adjacent tet n1 (outside B(p)).
    sym(deadtet, adjtet1);
    tspivot(deadtet, adjsh);
    // Find the edge in deadtet opposite to conpt.
    adjustedgering(deadtet, CCW);
    for (j = 0; j < 3; j++) {
      if (apex(deadtet) == conpt) break;
      enextself(deadtet);
    }
    assert(j < 3);
    // Get another adjacent tet n2.
    fnext(deadtet, adjtet2);
    symself(adjtet2);
    assert(adjtet2.tet != dummytet); // n2 is inside B(p).
    if (adjtet1.tet != dummytet) {
      bond(adjtet1, adjtet2); // Bond n1 <--> n2.
    } else {
      dissolve(adjtet2); // Dissolve at n2.
      dummytet[0] = encode(adjtet2); // Let dummytet holds n2.
    }
    if (adjsh.sh != dummysh) {
      tsbond(adjtet2, adjsh); // Bond s <--> n2.
    }
    // Collapse deadtet.
    tetrahedrondealloc(deadtet.tet);
  }
  deadtetlist->clear();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// deallocfaketets()    Deleted fake tets at fronts.                         //
//                                                                           //
// This routine is only called when the findrelocatepoint() routine fails.   //
// In other cases, the fake tets are removed automatically in carvecavity()  //
// or relocatepoint().                                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::deallocfaketets(list* frontlist)
{
  triface front, neightet;
  face checksh;
  bool infectflag;
  int i;

  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)(* frontlist)[i];
    // Let f face inside C. (f is a face of tet adjacent to C).
    adjustedgering(front, CW);
    sym(front, neightet);
    tspivot(front, checksh);
    if (oppo(front) == (point) NULL) {
      if (b->verbose > 2) {
        printf("    Get fake tet (%d, %d, %d).\n", pointmark(org(front)),
               pointmark(dest(front)), pointmark(apex(front)));
      }
      if (neightet.tet != dummytet) {
        // The neightet may be infected. After dissolve it, the infect flag
        //   will be lost. Save the flag and restore it later.
        infectflag = infected(neightet);
        dissolve(neightet);
        if (infectflag) {
          infect(neightet);
        }
      }
      if (checksh.sh != dummysh) {
        infectflag = sinfected(checksh);
        stdissolve(checksh);
        if (infectflag) {
          sinfect(checksh);
        }
      }
      // Dealloc the 'fake' tet.
      tetrahedrondealloc(front.tet);
      // If 'neightet' is a hull face, let 'dummytet' bond to it. It is
      //   a 'dummytet' when this front was created from a new subface.
      //   In such case, it should not be bounded.
      if (neightet.tet != dummytet) {
        dummytet[0] = encode(neightet);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restorepolyhedron()    Restore the tetrahedralization in a polyhedron.    //
//                                                                           //
// This routine is only called when the operation of suppressing a point is  //
// aborted (eg., findrelocatepoint() routine fails). The polyhedron has been //
// remeshed by new tets. This routine restore the old tets in it.            //
//                                                                           //
// 'oldtetlist' contains the list of old tets. Each old tet t_o assumes that //
// it still connects to a tet t_b of the mesh, however, t_b does not connect //
// to t_o, this routine resets the connection such that t_b <--> t_o.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::restorepolyhedron(list* oldtetlist)
{
  triface oldtet, neightet, neineitet;
  face checksh;
  int i;

  for (i = 0; i < oldtetlist->len(); i++) {
    // Get an old tet t_o.
    oldtet = * (triface *)(* oldtetlist)[i];
    // Check the four sides of t_o.
    for (oldtet.loc = 0; oldtet.loc < 4; oldtet.loc++) {
      sym(oldtet, neightet);
      tspivot(oldtet, checksh);
      if (neightet.tet != dummytet) {
        sym(neightet, neineitet);
        if (neineitet.tet != oldtet.tet) {
          // This face of t_o is a boundary of P.
          bond(neightet, oldtet);
          if (checksh.sh != dummysh) {
            tsbond(oldtet, checksh);
          }
        }
      } else {
        // t_o has a hull face. It should be the boundary of P.
#ifdef SELF_CHECK
        assert(checksh.sh != dummysh);
        stpivot(checksh, neineitet);
        assert(neineitet.tet != oldtet.tet);
#endif
        tsbond(oldtet, checksh);
        // Let dummytet[0] points to it.
        dummytet[0] = encode(oldtet);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppressfacetpoint()    Suppress a point inside a facet.                  //
//                                                                           //
// The point p inside a facet F will be suppressed from F by either being    //
// deleted from the mesh or being relocated into the volume.                 //
//                                                                           //
// 'supsh' is a subface f of F, and p = sapex(f); the other parameters are   //
// working lists which are empty at the beginning and the end.               //
//                                                                           //
// 'optflag' is used for mesh optimization. If it is set, after removing p,  //
// test the object function on each new tet, queue bad tets.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist,
  list* misfrontlist, list* ptlist, list* conlist, memorypool* viri,
  queue* flipque, bool noreloc, bool optflag)
{
  list *oldtetlist[2], *newtetlist[2];
  list *oldshlist, *newshlist;
  triface oldtet, newtet;
  face oldsh, newsh;
  point suppt, newpt[2];
  point *cons;
  REAL norm[3];
  bool success;
  int shmark;
  int i, j;

  suppt = sapex(*supsh);
  if (b->verbose > 1) {
    printf("    Suppress point %d in facet.\n", pointmark(suppt));
  }

  // Initialize working lists, variables.
  for (i = 0; i < 2; i++) {
    oldtetlist[i] = (list *) NULL;
    newtetlist[i] = (list *) NULL;
    newpt[i] = (point) NULL;
  }
  oldshlist = new list(sizeof(face), NULL, 256);
  newshlist = new list(sizeof(face), NULL, 256);
  success = true; // Assume p can be suppressed.

  // Find subs of C(p).
  oldshlist->append(supsh);
  formstarpolygon(suppt, oldshlist, ptlist);
  // Get the edges of C(p). They form a closed polygon.
  for (i = 0; i < oldshlist->len(); i++) {
    oldsh = * (face *)(* oldshlist)[i];    
    cons = (point *) conlist->append(NULL);
    cons[0] = sorg(oldsh);
    cons[1] = sdest(oldsh);
  }
  // Re-triangulate the old C(p).
  shmark = shellmark(*supsh);
  triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque);
  // Get new subs of C(p), remove protected segments.
  retrievenewsubs(newshlist, true);
  // Substitute the old C(p) with the new C(p)
  replacepolygonsubs(oldshlist, newshlist);
  // Clear work lists.
  ptlist->clear();
  conlist->clear();
  flipque->clear();
  viri->restart();

  // B(p) (tets with p as a vertex) has been separated into two parts
  //   (B_0(p) and B_1(p)) by F. Process them individually.
  for (i = 0; i < 2 && success; i++) { 
    if (i == 1) sesymself(*supsh);
    // Get a tet containing p.
    stpivot(*supsh, oldtet);
    // Is this part empty?
    if (oldtet.tet == dummytet) continue;
    // Allocate spaces for storing (old and new) B_i(p).
    oldtetlist[i] = new list(sizeof(triface), NULL, 256);
    newtetlist[i] = new list(sizeof(triface), NULL, 256);
    // Form old B_i(p) in oldtetlist[i].
    assert(!isdead(&oldtet));
    oldtetlist[i]->append(&oldtet);
    formstarpolyhedron(suppt, oldtetlist[i], ptlist, false);
    // Infect the tets in old B_i(p) (they're going to be delete).
    for (j = 0; j < oldtetlist[i]->len(); j++) {
      oldtet = * (triface *)(* (oldtetlist[i]))[j];
      infect(oldtet);
    }
    // Preparation for re-tetrahedralzing old B_i(p).
    orientnewsubs(newshlist, supsh, norm);
    // Tetrahedralize old B_i(p).
    success = constrainedcavity(&oldtet, newshlist, oldtetlist[i], ptlist,
                frontlist, misfrontlist, newtetlist[i], flipque);
    // If p is not suppressed, do relocation if 'noreloc' is not set.
    if (!success && !noreloc) {
      // Try to relocate p into the old B_i(p).
      makepoint(&(newpt[i]));
      success = findrelocatepoint(suppt, newpt[i], norm, frontlist,
                                  oldtetlist[i]);
      // Initialize newpt = suppt.
      // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j];
      // success = smoothvolpoint(newpt[i], frontlist, true);
      if (success) {
        // p is relocated by newpt[i]. Now insert it. Don't do flip since
        //   the new tets may get deleted again.
        relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL);
        setpointtype(newpt[i], FREEVOLVERTEX);
        relverts++;
      } else {
        // Fail to relocate p. Clean fake tets and quit this option.
        deallocfaketets(frontlist);
        pointdealloc(newpt[i]);
        newpt[i] = (point) NULL;
        assert(newtetlist[i]->len() == 0);
      }
    }
    if (!success && noreloc) {
      // Failed and no point relocation. Clean fake tets.
      deallocfaketets(frontlist);
    }
    // Clear work lists.
    ptlist->clear();
    frontlist->clear();
    misfrontlist->clear();
    flipque->clear();
  }

  if (success) {
    // p has been removed! (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    // Delete old C(p).
    for (i = 0; i < oldshlist->len(); i++) {
      oldsh = * (face *)(* oldshlist)[i];
      if (i == 0) {
        // Update the 'hullsize' if C(p) is on the hull.
        stpivot(oldsh, oldtet);
        if (oldtet.tet != dummytet) {
          sesymself(oldsh);
          stpivot(oldsh, oldtet);
        }
        if (oldtet.tet == dummytet) {
          // A boundary face. Update the 'hullsize'.
          j = oldshlist->len() - newshlist->len();
          assert(j > 0);
          hullsize -= j;
        }
      }
      shellfacedealloc(subfaces, oldsh.sh);
    }
    // Delete old B_i(p).
    for (i = 0; i < 2; i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Delete tets of the old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          assert(!isdead(&oldtet));
          tetrahedrondealloc(oldtet.tet);
        }
      }
    }
    if (optflag) {
      // Check for new bad-quality tets.
      for (i = 0; i < 2; i++) {
        if (newtetlist[i] != (list *) NULL) {
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            if (!isdead(&newtet)) checktet4opt(&newtet, true);
          }
        }
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    unsupverts++;
    // Restore the old C(p).
    replacepolygonsubs(newshlist, oldshlist);
    // Delete subs of the new C(p)
    for (i = 0; i < newshlist->len(); i++) {
      newsh = * (face *)(* newshlist)[i];
      shellfacedealloc(subfaces, newsh.sh);
    }
    // Restore old B_i(p).
    for (i = 0; i < 2; i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Uninfect tets of old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          assert(infected(oldtet));
          uninfect(oldtet);
        }
        // Has it been re-meshed?
        if (newtetlist[i]->len() > 0) {
          // Restore the old B_i(p).
          restorepolyhedron(oldtetlist[i]);
          // Delete tets of the new B_i(p);
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            // Some new tets may already be deleted (by carvecavity()).
            if (!isdead(&newtet)) {
              tetrahedrondealloc(newtet.tet);
            }
          }
        }
        // Dealloc newpt[i] if it exists.
        if (newpt[i] != (point) NULL) {
          pointdealloc(newpt[i]);
          relverts--;
        }
      }
    }
  }

  // Delete work lists.
  delete oldshlist;
  delete newshlist;
  for (i = 0; i < 2; i++) {
    if (oldtetlist[i] != (list *) NULL) {
      delete oldtetlist[i];
      delete newtetlist[i];
    }
  }

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppresssegpoint()    Suppress a point on a segment.                      //
//                                                                           //
// The point p on a segment S will be suppressed from S by either being      //
// deleted from the mesh or being relocated into the volume.                 //
//                                                                           //
// 'supseg' is the segment S, and p = sdest(S); the other parameters are     //
// working lists which are empty at the beginning and the end.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist,
  list* newsegshlist, list* frontlist, list* misfrontlist, list* ptlist,
  list* conlist, memorypool* viri, queue* flipque, bool noreloc, bool optflag)
{
  list **oldtetlist, **newtetlist;
  list **oldshlist, **newshlist;
  list *pnewshlist, *dnewshlist;
  triface oldtet, newtet;
  face oldsh, newsh;
  face startsh, spinsh, segsh1, segsh2;
  face nsupseg, newseg, prevseg, nextseg;
  point suppt, *newpt;
  point pa, pb, *cons;
  REAL pnorm[2][3], norm[3];
  bool success;
  int shmark;
  int n, i, j, k;

  // Get the Steiner point p.
  assert(supseg->shver < 2);
  suppt = sdest(*supseg);
  // Find the segment ab split by p.
  senext(*supseg, nsupseg);
  spivotself(nsupseg);
  assert(nsupseg.sh != dummysh);
  nsupseg.shver = 0;
  if (sorg(nsupseg) != suppt) sesymself(nsupseg);
  assert(sorg(nsupseg) == suppt);
  pa = sorg(*supseg);
  pb = sdest(nsupseg);
  if (b->verbose > 1) {
    printf("    Remove point %d on segment (%d, %d).\n",
           pointmark(suppt), pointmark(pa), pointmark(pb));
  }

  // Let startsh s containing p.
  spivot(*supseg, startsh);
  spinsh = startsh;
  do {
    // Save it in list.
    spinshlist->append(&spinsh);
    // Go to the next facet.
    spivotself(spinsh);
  } while (spinsh.sh != startsh.sh);
  if (spinshlist->len() == 1) {
    // This case has not handled yet.
    // printf("Unhandled case: segment only belongs to one facet.\n");
    spinshlist->clear();
    unsupverts++;
    return false;
  }

  // Suppose ab is shared by n facets (n > 1), then there are n B(p) (tets
  //   with p as a vertex). Some B(p) may be empty, eg, outside.
  n = spinshlist->len();
  oldtetlist = new list*[n];
  newtetlist = new list*[n];
  oldshlist = new list*[n];
  newshlist = new list*[n];
  newpt = new point[n];
  for (i = 0; i < n; i++) {
    oldtetlist[i] = (list *) NULL;
    newtetlist[i] = (list *) NULL;
    oldshlist[i] = (list *) NULL;
    newshlist[i] = (list *) NULL;
    newpt[i] = (point) NULL;
  }

  // Create a new segment ab (result in newseg).
  makeshellface(subsegs, &newseg);
  setsorg(newseg, pa);
  setsdest(newseg, pb);
  // ab gets the same mark and segment type as ap.
  setshellmark(newseg, shellmark(*supseg));
  setshelltype(newseg, shelltype(*supseg));
  if (b->quality && varconstraint) {
    // Copy the areabound into the new subsegment.
    setareabound(newseg, areabound(*supseg));
  }
  // Save the old connection at a.
  senext2(*supseg, prevseg);
  spivotself(prevseg);
  if (prevseg.sh != dummysh) {
    prevseg.shver = 0;
    if (sdest(prevseg) != pa) sesymself(prevseg);
    assert(sdest(prevseg) == pa);
    senextself(prevseg);
    senext2self(newseg);
    sbond(newseg, prevseg);
    newseg.shver = 0;
  }
  // Save the old connection at b.
  senext(nsupseg, nextseg);
  spivotself(nextseg);
  if (nextseg.sh != dummysh) {
    nextseg.shver = 0;
    if (sorg(nextseg) != pb) sesymself(nextseg);
    assert(sorg(nextseg) == pb);
    senext2self(nextseg);
    senextself(newseg);
    sbond(newseg, nextseg);
    newseg.shver = 0;
  }

  // Re-triangulate C(p) (subs with p as a vertex) to remove p.
  for (i = 0; i < spinshlist->len(); i++) {
    spinsh = * (face *)(* spinshlist)[i];
    // Allocate spaces for C_i(p).
    oldshlist[i] = new list(sizeof(face), NULL, 256);
    newshlist[i] = new list(sizeof(face), NULL, 256);
    // Get the subs of C_i(p).
    oldshlist[i]->append(&spinsh);
    formstarpolygon(suppt, oldshlist[i], ptlist);
    // Find the edges of C_i(p). It DOES NOT form a closed polygon.
    for (j = 0; j < oldshlist[i]->len(); j++) {
      oldsh = * (face *)(* (oldshlist[i]))[j];    
      cons = (point *) conlist->append(NULL);
      cons[0] = sorg(oldsh);
      cons[1] = sdest(oldsh);
    }
    // The C_i(p) isn't closed without ab. Add it to it.
    cons = (point *) conlist->append(NULL);
    cons[0] = pa;
    cons[1] = pb;
    // Re-triangulate C_i(p).
    shmark = shellmark(spinsh);
    triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque);
    // Get new subs of C_i(p), remove protected segments.
    retrievenewsubs(newshlist[i], true);
    // Substitute old C_i(p) with the new C_i(p). !IT IS NOT COMPLETE!
    replacepolygonsubs(oldshlist[i], newshlist[i]);
    // Find the new subface s having edge ab.
    for (j = 0; j < newshlist[i]->len(); j++) {
      segsh1 = * (face *)(* (newshlist[i]))[j];
      for (k = 0; k < 3; k++) {
        if (((sorg(segsh1) == pa) && (sdest(segsh1) == pb)) ||
            ((sorg(segsh1) == pb) && (sdest(segsh1) == pa))) break;
        senextself(segsh1);
      }
      if (k < 3) break; // Found.
    }
    assert(j < newshlist[i]->len()); // ab must exist.
    // Bond s and ab together. The C_i(p) is completedly substituted.
    ssbond(segsh1, newseg);
    // Save s for forming the face ring of ab.
    newsegshlist->append(&segsh1);
    // Clear work lists.
    ptlist->clear();
    conlist->clear();
    flipque->clear();
    viri->restart();
  }
  // Form the face ring of ab.
  for (i = 0; i < newsegshlist->len(); i++) {
    segsh1 = * (face *)(* newsegshlist)[i];
    if ((i + 1) == newsegshlist->len()) {
      segsh2 = * (face *)(* newsegshlist)[0];
    } else {
      segsh2 = * (face *)(* newsegshlist)[i + 1];
    }
    sbond1(segsh1, segsh2);
  }

  // A work list for keeping subfaces from two facets.
  dnewshlist = new list(sizeof(face), NULL, 256);
  success = true; // Assume p is suppressable.

  // Suppress p in all B(p). B_i(p) is looped wrt the right-hand rule of ab.
  for (i = 0; i < spinshlist->len() && success; i++) {
    // Get an old  subface s (ap) of a facet.
    spinsh = * (face *)(* spinshlist)[i];
    // Let the edge direction of s be a->b. Hence all subfaces follow
    //   the right-hand rule of ab.
    if (sorg(spinsh) != pa) sesymself(spinsh);
    // Get a tet t of B_i(p).
    stpivot(spinsh, oldtet);
    // Is B_i(p) empty?
    if (oldtet.tet == dummytet) continue;
    // Allocate spaces for B_i(p).
    oldtetlist[i] = new list(sizeof(triface), NULL, 256);
    newtetlist[i] = new list(sizeof(triface), NULL, 256);
    // Find all tets of old B_i(p).
    oldtetlist[i]->append(&oldtet);
    formstarpolyhedron(suppt, oldtetlist[i], ptlist, false);
    // Infect tets of old B_i(p) (they're going to be deleted).
    for (j = 0; j < oldtetlist[i]->len(); j++) {
      oldtet = * (triface *)(* (oldtetlist[i]))[j];
      infect(oldtet);
    }
    // Collect new subfaces (of two facets) bounded B_i(p).
    for (k = 0; k < 2; k++) {
      if ((i + k) < spinshlist->len()) {
        pnewshlist = newshlist[i + k];
        segsh1 = * (face *)(* spinshlist)[i + k];
      } else {
        pnewshlist = newshlist[0];
        segsh1 = * (face *)(* spinshlist)[0];
      }
      // Adjust the orientation of segsh1 to face to the inside of C.
      if (k == 0) {
        if (sorg(segsh1) != pa) sesymself(segsh1);
        assert(sorg(segsh1) == pa);
      } else {
        if (sdest(segsh1) != pa) sesymself(segsh1);
        assert(sdest(segsh1) == pa);
      }
      // Preparation for re-tetrahedralzing old B_i(p).
      orientnewsubs(pnewshlist, &segsh1, pnorm[k]);
      for (j = 0; j < pnewshlist->len(); j++) {
        dnewshlist->append((face *)(* pnewshlist)[j]);
      }
    }
    // Tetrahedralize B_i(p).
    success = constrainedcavity(&oldtet, dnewshlist, oldtetlist[i], ptlist,
                frontlist, misfrontlist, newtetlist[i], flipque);
    if (!success && !noreloc) {
      // C must be finished by re-locating the steiner point.
      makepoint(&(newpt[i]));
      for (j = 0; j < 3; j++) norm[j] = 0.5 * (pnorm[0][j] + pnorm[1][j]);
      success = findrelocatepoint(suppt, newpt[i], norm, frontlist,
                                  oldtetlist[i]);
      // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j];
      // success = smoothvolpoint(newpt[i], frontlist, true);
      if (success) {
        // p is relocated by newpt[i]. Now insert it. Don't do flip since
        //   the new tets may get deleted again.
        relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL);
        setpointtype(newpt[i], FREEVOLVERTEX);
        relverts++;
      } else {
        // Fail to relocate p. Clean fake tets and quit this option.
        deallocfaketets(frontlist);
        pointdealloc(newpt[i]);
        newpt[i] = (point) NULL;
        assert(newtetlist[i]->len() == 0);
      }
    }
    if (!success && noreloc) {
      // Failed and no point relocation. Clean fake tets.
      deallocfaketets(frontlist);
    }
    // Clear work lists.
    dnewshlist->clear();
    ptlist->clear();
    frontlist->clear();
    misfrontlist->clear();
    flipque->clear();
  }

  if (success) {
    // p has been suppressed. (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    // Update the segmnet pointers saved in a and b.
    setpoint2sh(pa, sencode(newseg));
    setpoint2sh(pb, sencode(newseg));
    // Delete old segments ap, pb.
    shellfacedealloc(subsegs, supseg->sh);
    shellfacedealloc(subsegs, nsupseg.sh);
    // Delete subs of old C_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      for (j = 0; j < oldshlist[i]->len(); j++) {
        oldsh = * (face *)(* (oldshlist[i]))[j];
        if (j == 0) {
          // Update 'hullsize' if C_i(p) is on the hull.
          stpivot(oldsh, oldtet);
          if (oldtet.tet != dummytet) {
            sesymself(oldsh);
            stpivot(oldsh, oldtet);
          }
          if (oldtet.tet == dummytet) {
            // Update 'hullsize'.
            k = oldshlist[i]->len() - newshlist[i]->len();
            assert(k > 0);
            hullsize -= k;
          }
        }
        shellfacedealloc(subfaces, oldsh.sh);
      }
    }
    // Delete tets old B_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      // Delete them if it is not empty.
      if (oldtetlist[i] != (list *) NULL) {
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          assert(!isdead(&oldtet));
          tetrahedrondealloc(oldtet.tet);
        }
      }
    }
    if (optflag) {
      for (i = 0; i < spinshlist->len(); i++) {
        // Check for new bad-quality tets.
        if (newtetlist[i] != (list *) NULL) {
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            if (!isdead(&newtet)) checktet4opt(&newtet, true);
          }
        }
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    unsupverts++;
    // Restore old connection at a.
    senext2(*supseg, prevseg);
    spivotself(prevseg);
    if (prevseg.sh != dummysh) {
      prevseg.shver = 0;
      if (sdest(prevseg) != pa) sesymself(prevseg);
      assert(sdest(prevseg) == pa);
      senextself(prevseg);
      senext2self(*supseg);
      sbond(*supseg, prevseg);
      senextself(*supseg); // Restore original state.
      assert(supseg->shver < 2);
    }
    // Restore old connection at b.
    senext(nsupseg, nextseg);
    spivotself(nextseg);
    if (nextseg.sh != dummysh) {
      nextseg.shver = 0;
      if (sorg(nextseg) != pb) sesymself(nextseg);
      assert(sorg(nextseg) == pb);
      senext2self(nextseg);
      senextself(nsupseg);
      sbond(nsupseg, nextseg);
      // nsupseg.shver = 0;
      senext2self(nsupseg); // Restore original state
      assert(nsupseg.shver < 2);
    }
    // Delete the new segment ab.
    shellfacedealloc(subsegs, newseg.sh);
    // Restore old C_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      replacepolygonsubs(newshlist[i], oldshlist[i]);
      // Delete subs of the new C_i(p)
      for (j = 0; j < newshlist[i]->len(); j++) {
        newsh = * (face *)(* (newshlist[i]))[j];
        shellfacedealloc(subfaces, newsh.sh);
      }
    }
    // Restore old B_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Uninfect tets of old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          assert(infected(oldtet));
          uninfect(oldtet);
        }
        // Has it been re-meshed?
        if (newtetlist[i]->len() > 0) {
          // Restore the old B_i(p).
          restorepolyhedron(oldtetlist[i]);
          // Delete tets of the new B_i(p);
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            // Some new tets may already be deleted (by carvecavity()).
            if (!isdead(&newtet)) {
              tetrahedrondealloc(newtet.tet);
            }
          }
        }
        // Dealloc newpt[i] if it exists.
        if (newpt[i] != (point) NULL) {
          pointdealloc(newpt[i]);
          relverts--;
        }
      }
    }
  }

  // Delete work lists.
  delete dnewshlist;
  for (i = 0; i < spinshlist->len(); i++) {
    delete oldshlist[i];
    delete newshlist[i];
  } 
  delete [] oldshlist;
  delete [] newshlist;
  for (i = 0; i < spinshlist->len(); i++) {
    if (oldtetlist[i] != (list *) NULL) {
      delete oldtetlist[i];
      delete newtetlist[i];
    }
  }
  delete [] oldtetlist;
  delete [] newtetlist;
  // Clear work lists.
  newsegshlist->clear();
  spinshlist->clear();

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppressvolpoint()    Suppress a point inside mesh.                       //
//                                                                           //
// The point p = org(suptet) is inside the mesh and will be suppressed from  //
// the mesh. Note that p may not be suppressed.                              //
//                                                                           //
// 'optflag' is used for mesh optimization. If it is set, after removing p,  //
// test the object function on each new tet, queue bad tets.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist,
  list* misfrontlist, list* ptlist, queue* flipque, bool optflag)
{
  list *myfrontlist, *mymisfrontlist, *myptlist;
  list *oldtetlist, *newtetlist;
  list *newshlist; // a dummy list.
  queue *myflipque;
  triface oldtet, newtet;
  point suppt, conpt;
  bool success;
  int j;

  // Allocate spaces for storing (old and new) B(p).
  oldtetlist = new list(sizeof(triface), NULL, 256);
  newtetlist = new list(sizeof(triface), NULL, 256);
  newshlist = new list(sizeof(face), NULL, 256);
  // Allocate work lists if user doesn't supply them.
  myfrontlist = mymisfrontlist = myptlist = (list *) NULL;
  myflipque = (queue *) NULL;
  if (frontlist == (list *) NULL) {
    myfrontlist = new list(sizeof(triface), NULL, 256);
    frontlist = myfrontlist;
    mymisfrontlist = new list(sizeof(triface), NULL, 256);
    misfrontlist = mymisfrontlist;
    myptlist = new list(sizeof(point *), NULL, 256);
    ptlist = myptlist;
    myflipque = new queue(sizeof(badface));
    flipque = myflipque;
  }

  suppt = org(*suptet);
  oldtet = *suptet;
  success = true; // Assume p can be suppressed.

  if (b->verbose > 1) {
    printf("    Remove point %d in mesh.\n", pointmark(suppt));
  }

  // Form old B(p) in oldtetlist.
  oldtetlist->append(&oldtet);
  formstarpolyhedron(suppt, oldtetlist, ptlist, false);
  // Infect the tets in old B(p) (they're going to be delete).
  for (j = 0; j < oldtetlist->len(); j++) {
    oldtet = * (triface *)(* oldtetlist)[j];
    infect(oldtet);
  }
  // Tetrahedralize old B(p).
  success = constrainedcavity(&oldtet, newshlist, oldtetlist, ptlist,
              frontlist, misfrontlist, newtetlist, flipque);
  if (!success) {
    // Unable to suppress p.
    deallocfaketets(frontlist);
    // Try to collapse an edge at p. 
    conpt = (point) NULL;
    assert(newtetlist->len() == 0);
    if (findcollapseedge(suppt, &conpt, oldtetlist, ptlist)) {
      // Collapse the edge suppt->conpt. Re-use newtetlist.
      collapseedge(suppt, conpt, oldtetlist, newtetlist);
      // The oldtetlist contains newtetlist.
      if (optflag) {
        assert(newtetlist->len() == 0);
        for (j = 0; j < oldtetlist->len(); j++) {
          newtet = * (triface *)(* oldtetlist)[j];
          newtetlist->append(&newtet);
        }
      }
      oldtetlist->clear(); // Do not delete them.
      collapverts++;
      success = true;
    }
  }
  if (success) {
    // p has been removed! (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    suprelverts++;
    // Delete old B(p).
    for (j = 0; j < oldtetlist->len(); j++) {
      oldtet = * (triface *)(* oldtetlist)[j];
      assert(!isdead(&oldtet));
      tetrahedrondealloc(oldtet.tet);
    }
    if (optflag) {
      // Check for new bad tets.
      for (j = 0; j < newtetlist->len(); j++) {
        newtet = * (triface *)(* newtetlist)[j];
        if (!isdead(&newtet)) checktet4opt(&newtet, true);
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    // Uninfect tets of old B(p).
    for (j = 0; j < oldtetlist->len(); j++) {
      oldtet = * (triface *)(* oldtetlist)[j];
      assert(infected(oldtet));
      uninfect(oldtet);
    }
  }

  // Clear work lists.
  ptlist->clear();
  frontlist->clear();
  misfrontlist->clear();
  flipque->clear();
  // Deallocate work lists.
  if (myfrontlist != (list *) NULL) {
    delete myfrontlist;
    delete mymisfrontlist;
    delete myptlist;
    delete myflipque;
  }
  delete oldtetlist;
  delete newtetlist;
  delete newshlist;

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// smoothpoint()    Smooth a volume/segment point.                           //
//                                                                           //
// 'smthpt' (p) is inside the polyhedron (C) bounded by faces in 'starlist'. //
// This routine moves p inside C until an object function is maximized.      //
//                                                                           //
// Default, the CCW edge ring of the faces on C points to p. If 'invtori' is //
// TRUE, the orientation is inversed.                                        //
//                                                                           //
// If 'key' != NULL, it contains an object value to be improved. Current it  //
// means the cosine of the largest dihedral angle. In such case, the point   //
// is smoothed only if the final configuration improves the object value, it //
// is returned by the 'key'.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::smoothpoint(point smthpt, point e1, point e2, list *starlist,
  bool invtori, REAL *key)
{
  triface starttet;
  point pa, pb, pc;
  REAL fcent[3], startpt[3], nextpt[3], bestpt[3];
  REAL iniTmax, oldTmax, newTmax;
  REAL ori, aspT, aspTmax, imprate;
  REAL cosd, maxcosd;
  bool segflag, randflag; //, subflag; 
  int numdirs;
  int iter, i, j;

  // Is p a segment vertex?
  segflag = (e1 != (point) NULL);
  // Decide the number of moving directions.
  numdirs = segflag ? 2 : starlist->len();
  randflag = numdirs > 10;
  if (randflag) {
    numdirs = 10; // Maximum 10 directions.
  }

  // Calculate the initial object value (the largest aspect ratio).
  for (i = 0; i < starlist->len(); i++) {
    starttet = * (triface *)(* starlist)[i];
    adjustedgering(starttet, !invtori ? CCW : CW);
    pa = org(starttet);
    pb = dest(starttet);
    pc = apex(starttet);
    aspT = tetaspectratio(pa, pb, pc, smthpt);
    if (i == 0) {
      aspTmax = aspT;
    } else {
      aspTmax = aspT > aspTmax ? aspT : aspTmax;
    }
  }
  iniTmax = aspTmax;

  if (b->verbose > 1) {
    printf("    Smooth %s point %d (%g, %g, %g).\n", segflag ? "seg" : "vol",
           pointmark(smthpt), smthpt[0], smthpt[1], smthpt[2]);
    printf("    Initial max L/h = %g.\n", iniTmax);
  }
  for (i = 0; i < 3; i++) {
    bestpt[i] = startpt[i] = smthpt[i];
  }

  // Do iteration until the new aspTmax does not decrease.
  newTmax = iniTmax;
  iter = 0;
  while (true) {
    // Find the best next location.
    oldTmax = newTmax;
    for (i = 0; i < numdirs; i++) {
      // Calculate the moved point (saved in 'nextpt').
      if (!segflag) {
        if (randflag) {
          // Randomly pick a direction.
          j = (int) randomnation(starlist->len());
        } else {
          j = i;
        }
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        for (j = 0; j < 3; j++) {
          fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0;
        }
      } else {
        for (j = 0; j < 3; j++) {
          fcent[j] = (i == 0 ? e1[j] : e2[j]);
        }
      }
      for (j = 0; j < 3; j++) {
        nextpt[j] = startpt[j] + 0.01 * (fcent[j] - startpt[j]); 
      }
      // Get the largest object value for the new location.
      for (j = 0; j < starlist->len(); j++) {
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        ori = orient3d(pa, pb, pc, nextpt);
        if (ori < 0.0) {
          aspT = tetaspectratio(pa, pb, pc, nextpt);
          if (j == 0) {
            aspTmax = aspT;
          } else {
            aspTmax = aspT > aspTmax ? aspT : aspTmax;
          }
        } else {
          // An invalid new tet. Discard this point.
          aspTmax = newTmax;
        } // if (ori < 0.0)
        // Stop looping when the object value is bigger than before.
        if (aspTmax >= newTmax) break;
      } // for (j = 0; j < starlist->len(); j++)
      if (aspTmax < newTmax) {
        // Save the improved object value and the location.
        newTmax = aspTmax;
        for (j = 0; j < 3; j++) bestpt[j] = nextpt[j];
      }
    } // for (i = 0; i < starlist->len(); i++)
    // Does the object value improved much?
    imprate = fabs(oldTmax - newTmax) / oldTmax;
    if (imprate < 1e-3) break;
    // Yes, move p to the new location and continue.
    for (j = 0; j < 3; j++) startpt[j] = bestpt[j];
    iter++;
  } // while (true)

  if (iter > 0) {
    // The point is moved.
    if (key) {
      // Check if the quality is improved by the smoothed point.
      maxcosd = 0.0; // = cos(90).
      for (j = 0; j < starlist->len(); j++) {
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        tetalldihedral(pa, pb, pc, startpt, NULL, &cosd, NULL);
        if (cosd < *key) {
          // This quality will not be improved. Stop.
          iter = 0; break;
        } else {
          // Remeber the worst quality value (of the new configuration).
          maxcosd = maxcosd < cosd ? maxcosd : cosd;
        }
      }
      if (iter > 0) *key = maxcosd;
    }
  }

  if (iter > 0) {
    segflag ? smoothsegverts++ : smoothvolverts++;
    for (i = 0; i < 3; i++) smthpt[i] = startpt[i];
    if (b->verbose > 1) {
      printf("    Move to new location (%g, %g, %g).\n", smthpt[0], smthpt[1],
             smthpt[2]);
      printf("    Final max L/h = %g. (%d iterations)\n", newTmax, iter);
      if (key) {
        printf("    Max. dihed = %g (degree).\n", acos(*key) / PI * 180.0);
      }
    }
    return true;
  } else {
    if (b->verbose > 1) {
      printf("    Not smoothed.\n");
    }
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removesteiners()    Delete or relocate Steiner points on facets.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::removesteiners(bool coarseflag)
{
  list *frontlist, *misfrontlist;
  list *spinshlist, *newsegshlist;
  list *ptlist, *conlist;
  memorypool *viri;
  queue *flipque;
  triface checktet;
  face shloop;
  face segloop, nextseg;
  point pa, neipt;
  REAL len;
  bool remflag;
  int *worklist;
  int oldnum, rmstein;
  int i, j;

  if (!b->quiet) {
    if (!coarseflag) {
      printf("Removing Steiner points.\n");
    } else {
      printf("Coarsening mesh.\n");
    }
  }

  // Initialize work lists.
  frontlist = new list(sizeof(triface), NULL);
  misfrontlist = new list(sizeof(triface), NULL);
  spinshlist = new list(sizeof(face), NULL);
  newsegshlist = new list(sizeof(face), NULL);
  ptlist = new list(sizeof(point *), NULL);
  conlist = new list(sizeof(point *) * 2, NULL);
  flipque = new queue(sizeof(badface));
  viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0);
  oldnum = unuverts;
  relverts = suprelverts = collapverts = unsupverts;
  smoothvolverts = 0;
  expcavcount = 0;

  // Suppress Steiner points inside facets.
  do {
    rmstein = unuverts;
    subfaces->traversalinit();
    shloop.sh = shellfacetraverse(subfaces);
    while (shloop.sh != (shellface *) NULL) {
      remflag = false;
      // Is s contains a Steiner point?
      shloop.shver = 0;
      for (i = 0; i < 3; i++) {
        pa = sapex(shloop);
        if (pointtype(pa) == FREESUBVERTEX) {
          if (!coarseflag) {
            // Remove it if it is not an input point.
            j = pointmark(pa) - in->firstnumber;
            if (j >= in->numberofpoints) {
              if (b->nobisect == 1) {
                // '-Y'. Remove p if s is a hull face.
                stpivot(shloop, checktet);
                if (checktet.tet != dummytet) {
                  sesymself(shloop);
                  stpivot(shloop, checktet);
                }
                remflag = (checktet.tet == dummytet);
              } else {
                // '-YY'. Remove p whatever s is a hull face or not.
                remflag = true;
              }
            }
          } else {
            // Check if this vertex can be coarsed.
            if (b->nobisect == 0) {
              // Is a background mesh available?
              if (b->metric) {
                // assert(pa[pointmtrindex] > 0.0);
                // Form the star of pa.
                spinshlist->append(&shloop);
                formstarpolygon(pa, spinshlist, ptlist);
                len = 0.0;
                for (j = 0; j < ptlist->len(); j++) {
                  neipt = * (point *)(* ptlist)[j];
                  len += distance(pa, neipt);
                }
                len /= ptlist->len();
                // Carse it if the average edge length is small.
                remflag = len < pa[pointmtrindex];
                spinshlist->clear();
                ptlist->clear();
              } else {
                // Coarse it if (1) it is an input point and its pointmarker
                //   is zero, or (2) it is a Steiner point.
                remflag = true;
                j = pointmark(pa) - in->firstnumber;
                if (j < in->numberofpoints) {
                  remflag = (in->pointmarkerlist[j] == 0);
                }
              } // if (b->metric)
            } // if (b->nobisect == 0)
          } // if (!coarseflag)
          if (remflag) break;
        } // if (pointtype(pa) == FREESUBVERTEX)
        senextself(shloop);
      } // for (i = 0; i < 3; i++)
      if (remflag) {
        suppressfacetpoint(&shloop, frontlist, misfrontlist, ptlist, conlist,
                           viri, flipque, coarseflag, false);
      }
      shloop.sh = shellfacetraverse(subfaces);
    }
    // Continue if any Steiner point has been removed.
  } while (unuverts > rmstein);

  if (coarseflag) {
    shellface **segsperverlist;
    int *idx2seglist;
    face seg1, seg2;
    point e1, e2;
    // Connecting collinear segments. Hence the segment vertices may be
    //   removed. In fact, this should be done by reconstructmesh().
    makesegmentmap(idx2seglist, segsperverlist);
    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != (shellface *) NULL) {
      for (i = 0; i < 2; i++) {
        segloop.shver = i;
        senext(segloop, nextseg);
        spivotself(nextseg);
        if ((nextseg.sh == dummysh) || (nextseg.sh > segloop.sh)) {
          // No neighbor segment connection or haven't been processed yet.
          pa = sdest(segloop);
          j = pointmark(pa) - in->firstnumber;
          if (idx2seglist[j + 1] - idx2seglist[j] == 2) {
            // pa is shared by only two segments. Get the other one.
            nextseg.sh = segsperverlist[idx2seglist[j]];
            if (nextseg.sh == segloop.sh) {
              nextseg.sh = segsperverlist[idx2seglist[j] + 1];
            }
            nextseg.shver = 0;
            if (sorg(nextseg) != pa) sesymself(nextseg);
            // Check if the two segments are collinear.
            e1 = sorg(segloop);
            e2 = sdest(nextseg);
            if (iscollinear(e1, pa, e2, b->epsilon)) {
              // Connect the two segments together.
              if (b->verbose > 1) {
                printf("  Glue two insegs (%d, %d) at %d.\n", pointmark(e1),
                       pointmark(e2), pointmark(pa));
              }
              senext(segloop, seg1);
              senext2(nextseg, seg2);
              sbond(seg1, seg2);
            }
          }
        } // if (nextseg.sh == dummysh)
      } // for (i = 0;
      segloop.sh = shellfacetraverse(subsegs);
    }
    delete [] segsperverlist;
    delete [] idx2seglist;
  }

  // Suppress Steiner points on segments.
  do {
    rmstein = unuverts;
    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != (shellface *) NULL) {
      remflag = false;
      // for (i = 0; i < 2; i++) {
        // Don't check the poinytype of pa, it may be a Steiner point but
        //   has type NACUTEVERTEX due to splitting a type-3 segment.
        segloop.shver = 0; // segloop.shver = i;
        senext(segloop, nextseg);
        spivotself(nextseg);
        if (nextseg.sh != dummysh) {
          pa = sdest(segloop); // p is going to be checked for removal.
          nextseg.shver = 0;
          if (sorg(nextseg) != pa) sesymself(nextseg);  
          assert(sorg(nextseg) == pa);
          if (!coarseflag) {
            // try to remove it if it is not an input point.
            j = pointmark(pa) - in->firstnumber;
            if (j >= in->numberofpoints) {
              if (b->nobisect == 1) {
                // '-Y'. Remove p if it is on the hull.
                sstpivot(&segloop, &checktet);
                assert(checktet.tet != dummytet);
                pa = apex(checktet);
                do {
                  if (!fnextself(checktet)) {
                    // Meet a boundary face - p is on the hull.
                    remflag = true; break;
                  }
                } while (pa != apex(checktet));
              } else {
                // '-YY'. Remove p whatever it is on the hull or not.
                remflag = true;
              }
            }
          } else {
            // Check if this vertex can be coarsed.
            if (b->nobisect == 0) {
              if (b->metric) {
                // assert(pa[pointmtrindex] > 0.0);
                len = 0.0;
                neipt = sorg(segloop);
                for (j = 0; j < 2; j++) {
                  len += distance(pa, neipt);
                  /*// Is neipt inside the sparse ball of pa?
                  if (len < pa[pointmtrindex]) {
                    // Yes, the local of pa is too dense, corse it.
                    remflag = true; break;
                  } */
                  neipt = sdest(nextseg);
                }
                len /= 2.0;
                // Carse it if the average edge lengh is small.
                remflag = len < pa[pointmtrindex]; 
              } else {
                // Coarse it if (1) it is an input point and its pointmarker
                //   is zero, or (2) it is a Steiner point.
                remflag = true;
                j = pointmark(pa) - in->firstnumber;
                if (j < in->numberofpoints) {
                  remflag = (in->pointmarkerlist[j] == 0);
                }
              } // if (b->metric)
            } // if (b->nobisect == 0)
          } // if (!coarseflag)
        } // if (nextseg.sh != dummysh)
        // if (remflag) break;
      // } // for (i = 0; i < 2; i++)
      if (remflag) {
        suppresssegpoint(&segloop, spinshlist, newsegshlist, frontlist,
          misfrontlist, ptlist, conlist, viri, flipque, coarseflag, false);
      }
      segloop.sh = shellfacetraverse(subsegs);
    }
    // Continue if any Steiner point has been removed.
  } while (unuverts > rmstein);

  if ((relverts > 0) || coarseflag) {
    worklist = new int[points->items + 1];
    // Suppress relocated points & coarse free mesh points.
    do {
      // Initialize the work list. Each entry of the list counts how many
      //   times the point has been processed.
      for (i = 0; i < points->items + 1; i++) worklist[i] = 0;
      rmstein = unuverts;
      tetrahedrons->traversalinit();
      checktet.tet = tetrahedrontraverse();
      while (checktet.tet != (tetrahedron *) NULL) {
        remflag = false;
        for (i = 0; i < 4; i++) {
          pa = (point) checktet.tet[4 + i];
          if (pointtype(pa) == FREEVOLVERTEX) {
            // NOTE. Chenge the number 3 will change the number n of removed
            //   Steiner points. In my test, n is larger when it is 1. 3
            //   reduces n in a reasonable way (see example, mech_part,
            //   thepart), 5 results a larger n than 3 does. While the best
            //   result is no limit of this number, but it makes the code
            //   extremely slow.
            if (worklist[pointmark(pa)] < 3) {
              worklist[pointmark(pa)]++;
              if (!coarseflag) {
                // Remove p if it is a Steiner point.
                if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) {
                  remflag = true;
                }
              } else {
                if (b->metric) {
                  // assert(pa[pointmtrindex] > 0.0);
                  // Form the star of pa.
                  frontlist->append(&checktet);
                  formstarpolyhedron(pa, frontlist, ptlist, true);
                  len = 0.0;
                  for (j = 0; j < ptlist->len(); j++) {
                    neipt = * (point *)(* ptlist)[j];
                    len += distance(pa, neipt);
                  }
                  len /= ptlist->len();
                  // Carse it if the average edge length is small.
                  remflag = len < pa[pointmtrindex];
                  frontlist->clear();
                  ptlist->clear();
                } else {
                  // Coarse it if (1) it is an input point and its pointmarker
                  //   is zero, or (2) it is a Steiner point.
                  remflag = true;
                  j = pointmark(pa) - in->firstnumber;
                  if (j < in->numberofpoints) {
                    remflag = (in->pointmarkerlist[j] == 0);
                  }
                } // if (b->metric)
              } // if (!coarseflag)
              if (remflag) break;
            } // if (worklist[pointmark(pa)] == 0)
          } // if (pointtype(pa) == FREEVOLVERTEX)
        } // for (i = 0; i < 4; i++)
        if (remflag) {
          findorg(&checktet, pa);
          assert(org(checktet) == pa);
          suppressvolpoint(&checktet, frontlist, misfrontlist, ptlist, flipque,
                           false);
        }
        checktet.tet = tetrahedrontraverse();
      }
      // Continue if any relocated point has been suppressed.
    } while (unuverts > rmstein);


    // Smooth the unsuppressed points if it is not coarse mesh.
    if (!coarseflag && (relverts > suprelverts)) {
      if (b->verbose) {
        printf("  Smoothing relocated points.\n");
      }
      for (i = 0; i < points->items + 1; i++) worklist[i] = 0;
      tetrahedrons->traversalinit();
      checktet.tet = tetrahedrontraverse();
      while (checktet.tet != (tetrahedron *) NULL) {
        for (i = 0; i < 4; i++) {
          pa = (point) checktet.tet[4 + i];
          if (pointtype(pa) == FREEVOLVERTEX) {
            if (worklist[pointmark(pa)] == 0) {
              worklist[pointmark(pa)] = 1;
              if (pointmark(pa) >= (in->numberofpoints + in->firstnumber)) {
                // Smooth pa.
                findorg(&checktet, pa);
                frontlist->append(&checktet);
                formstarpolyhedron(pa, frontlist, NULL, false);
                smoothpoint(pa, NULL, NULL, frontlist, false, NULL);
                frontlist->clear();
              }
            } // if (worklist[pointmark(pa)] == 0)
          } // if (pointtype(pa) == FREEVOLVERTEX)
        } // for (i = 0; i < 4; i++)
        checktet.tet = tetrahedrontraverse();
      }
    }
    delete [] worklist;
  }

  if (b->verbose > 0) {
    if (!coarseflag) {
      printf("  %d points removed from boundary", unuverts - oldnum);
      if (expcavcount > 0) {
        printf(" (%d cavity corrections)", expcavcount);
      }
      printf("\n");
      if (relverts > 0) {
        printf("  %d points relocated (%d suppressed, %d collapsed).\n",
               relverts, suprelverts - collapverts, collapverts);
        if (smoothvolverts > 0) {
          printf("  %d points are smoothed.\n", smoothvolverts);
        }
      }
      if (unsupverts > 0) {
        printf("  !! %d points are unsuppressed.\n", unsupverts);
      }
    } else {
      printf("  %d points are removed.\n", unuverts - oldnum);
    }
  }

  // Delete work lists.
  delete frontlist;
  delete misfrontlist;
  delete spinshlist;
  delete newsegshlist;
  delete ptlist;
  delete conlist;
  delete flipque;
  delete viri;
}

//
// End of boundary Steiner points removing routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// reconstructmesh()    Reconstruct a tetrahedral mesh from a list of        //
//                      tetrahedra and possibly a list of boundary faces.    //
//                                                                           //
// The list of tetrahedra is stored in 'in->tetrahedronlist',  the list of   //
// boundary faces is stored in 'in->trifacelist'.  The tetrahedral mesh is   //
// reconstructed in memorypool 'tetrahedrons', its boundary faces (subfaces) //
// are reconstructed in 'subfaces', its boundary edges (subsegments) are     //
// reconstructed in 'subsegs'. If the -a switch is used, this procedure will //
// also read a list of REALs from 'in->tetrahedronvolumelist' and set a      //
// maximum volume constraint on each tetrahedron.                            //
//                                                                           //
// If the user has provided the boundary faces in 'in->trifacelist', they    //
// will be inserted the mesh. Otherwise subfaces will be identified from the //
// mesh.  All hull faces (including faces of the internal holes) will be     //
// recognized as subfaces, internal faces between two tetrahedra which have  //
// different attributes will also be recognized as subfaces.                 //
//                                                                           //
// Subsegments will be identified after subfaces are reconstructed. Edges at //
// the intersections of non-coplanar subfaces are recognized as subsegments. //
// Edges between two coplanar subfaces with different boundary markers are   //
// also recognized as subsegments.                                           //
//                                                                           //
// The facet index of each subface will be set automatically after we have   //
// recovered subfaces and subsegments.  That is, the set of subfaces, which  //
// are coplanar and have the same boundary marker will be recognized as a    //
// facet and has a unique index, stored as the facet marker in each subface  //
// of the set, the real boundary marker of each subface will be found in     //
// 'in->facetmarkerlist' by the index.  Facet index will be used in Delaunay //
// refinement for detecting two incident facets.                             //
//                                                                           //
// Points which are not corners of tetrahedra will be inserted into the mesh.//
// Return the number of faces on the hull after the reconstruction.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::reconstructmesh()
{
  tetrahedron **tetsperverlist;
  shellface **facesperverlist;
  triface tetloop, neightet, neineightet, spintet;
  face subloop, neighsh, neineighsh, subseg;
  face sface1, sface2;
  point *idx2verlist;
  point torg, tdest, tapex, toppo;
  point norg, ndest, napex;
  list *neighshlist, *markerlist;
  REAL sign, attrib, volume;
  REAL da1, da2;
  bool bondflag, insertsegflag;
  int *idx2tetlist;
  int *idx2facelist;
  int *worklist;
  int facetidx, marker;
  int iorg, idest, iapex, ioppo;
  int inorg, indest, inapex;
  int index, i, j;

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

  // Create a map from index to points.
  makeindex2pointmap(idx2verlist);

  // Create the tetrahedra.
  for (i = 0; i < in->numberoftetrahedra; i++) {
    // Create a new tetrahedron and set its four corners, make sure that
    //   four corners form a positive orientation.
    maketetrahedron(&tetloop);
    index = i * in->numberofcorners;
    // Although there may be 10 nodes, we only read the first 4.
    iorg = in->tetrahedronlist[index] - in->firstnumber;
    idest = in->tetrahedronlist[index + 1] - in->firstnumber;
    iapex = in->tetrahedronlist[index + 2] - in->firstnumber;
    ioppo = in->tetrahedronlist[index + 3] - in->firstnumber;
    torg = idx2verlist[iorg];
    tdest = idx2verlist[idest];
    tapex = idx2verlist[iapex];
    toppo = idx2verlist[ioppo];
    sign = orient3d(torg, tdest, tapex, toppo);
    if (sign > 0.0) {
      norg = torg; torg = tdest; tdest = norg;
    } else if (sign == 0.0) {
      if (!b->quiet) {
        printf("Warning:  Tet %d is degenerate.\n", i + in->firstnumber);
      }
    }
    setorg(tetloop, torg);
    setdest(tetloop, tdest);
    setapex(tetloop, tapex);
    setoppo(tetloop, toppo);
    // Temporarily set the vertices be type FREEVOLVERTEX, to indicate that
    //   they belong to the mesh.  These types may be changed later.
    setpointtype(torg, FREEVOLVERTEX);
    setpointtype(tdest, FREEVOLVERTEX);
    setpointtype(tapex, FREEVOLVERTEX);
    setpointtype(toppo, FREEVOLVERTEX);
    // Set element attributes if they exist.
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      index = i * in->numberoftetrahedronattributes;
      attrib = in->tetrahedronattributelist[index + 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);
    }
  }

  // Set the connection between tetrahedra.
  hullsize = 0l;
  // Create a map from nodes to tetrahedra.
  maketetrahedronmap(idx2tetlist, tetsperverlist);
  // Initialize the worklist.
  worklist = new int[points->items];
  for (i = 0; i < points->items; i++) worklist[i] = 0;

  // Loop all tetrahedra, bond two tetrahedra if they share a common face.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Loop the four sides of the tetrahedron.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, neightet);
      if (neightet.tet != dummytet) continue; // This side has finished.
      torg = org(tetloop);
      tdest = dest(tetloop);
      tapex = apex(tetloop);
      iorg = pointmark(torg) - in->firstnumber;
      idest = pointmark(tdest) - in->firstnumber;
      iapex = pointmark(tapex) - in->firstnumber;
      worklist[iorg] = 1;
      worklist[idest] = 1;
      worklist[iapex] = 1;
      bondflag = false;
      // Search its neighbor in the adjacent tets of torg.
      for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; 
           j++) {
        if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself.
        neightet.tet = tetsperverlist[j];
        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
          sym(neightet, neineightet);
          if (neineightet.tet == dummytet) {
            norg = org(neightet);
            ndest = dest(neightet);
            napex = apex(neightet);
            inorg = pointmark(norg) - in->firstnumber;
            indest = pointmark(ndest) - in->firstnumber;
            inapex = pointmark(napex) - in->firstnumber;
            if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) {
              // Find! Bond them together and break the loop.
              bond(tetloop, neightet);
              bondflag = true;
              break;
            }
          }
        }
      }
      if (!bondflag) {
        hullsize++;  // It's a hull face.
        // Bond this side to outer space.
        dummytet[0] = encode(tetloop);
        if ((in->pointmarkerlist != (int *) NULL) && !b->coarse) {
          // Set its three corners's markers be boundary (hull) vertices.
          if (in->pointmarkerlist[iorg] == 0) {
            in->pointmarkerlist[iorg] = 1;
          }
          if (in->pointmarkerlist[idest] == 0) {
            in->pointmarkerlist[idest] = 1;
          }
          if (in->pointmarkerlist[iapex] == 0) {
            in->pointmarkerlist[iapex] = 1;
          }
        }
      }
      worklist[iorg] = 0;
      worklist[idest] = 0;
      worklist[iapex] = 0;
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Subfaces will be inserted into the mesh. It has two phases:
  //   (1) Insert subfaces provided by user (in->trifacelist);
  //   (2) Create subfaces for hull faces (if they're not subface yet) and
  //       interior faces which separate two different materials.

  // Phase (1). Is there a list of user-provided subfaces?
  if (in->trifacelist != (int *) NULL) {
    // Recover subfaces from 'in->trifacelist'.
    for (i = 0; i < in->numberoftrifaces; i++) {
      index = i * 3;
      iorg = in->trifacelist[index] - in->firstnumber;
      idest = in->trifacelist[index + 1] - in->firstnumber;
      iapex = in->trifacelist[index + 2] - in->firstnumber;
      // Look for the location of this subface.
      worklist[iorg] = 1;
      worklist[idest] = 1;
      worklist[iapex] = 1;
      bondflag = false;
      // Search its neighbor in the adjacent tets of torg.
      for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; 
           j++) {
        neightet.tet = tetsperverlist[j];
        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
          norg = org(neightet);
          ndest = dest(neightet);
          napex = apex(neightet);
          inorg = pointmark(norg) - in->firstnumber;
          indest = pointmark(ndest) - in->firstnumber;
          inapex = pointmark(napex) - in->firstnumber;
          if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) {
            bondflag = true;  // Find!
            break;
          }
        }
      }
      if (bondflag) {
        // Create a new subface and insert it into the mesh.
        makeshellface(subfaces, &subloop);
        torg = idx2verlist[iorg];
        tdest = idx2verlist[idest];
        tapex = idx2verlist[iapex];
        setsorg(subloop, torg);
        setsdest(subloop, tdest);
        setsapex(subloop, tapex);
        // Set the vertices be FREESUBVERTEX to indicate they belong to a
        //   facet of the domain.  They may be changed later.
        setpointtype(torg, FREESUBVERTEX);
        setpointtype(tdest, FREESUBVERTEX);
        setpointtype(tapex, FREESUBVERTEX);
        if (in->trifacemarkerlist != (int *) NULL) {
          setshellmark(subloop, in->trifacemarkerlist[i]);
        }
        adjustedgering(neightet, CCW);
        findedge(&subloop, org(neightet), dest(neightet));
        tsbond(neightet, subloop);
        sym(neightet, neineightet);
        if (neineightet.tet != dummytet) {
          sesymself(subloop);
          tsbond(neineightet, subloop);
        }
      } else {
        if (!b->quiet) {
          printf("Warning:  Subface %d is discarded.\n", i + in->firstnumber);
        }
      }
      worklist[iorg] = 0;
      worklist[idest] = 0;
      worklist[iapex] = 0;
    }
  } 

  // Phase (2). Indentify subfaces from the mesh.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Loop the four sides of the tetrahedron.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      tspivot(tetloop, subloop);
      if (subloop.sh != dummysh) continue;
      bondflag = false;
      sym(tetloop, neightet);
      if (neightet.tet == dummytet) {
        // It's a hull face. Insert a subface at here.
        bondflag = true;
      } else {
        // It's an interior face. Insert a subface if two tetrahedra have
        //   different attributes (i.e., they belong to two regions).
        if (in->numberoftetrahedronattributes > 0) {
          if (elemattribute(neightet.tet,
              in->numberoftetrahedronattributes - 1) != 
              elemattribute(tetloop.tet,
              in->numberoftetrahedronattributes - 1)) {
            bondflag = true;
          }
        }
      }
      if (bondflag) {
        adjustedgering(tetloop, CCW);
        makeshellface(subfaces, &subloop);
        torg = org(tetloop);
        tdest = dest(tetloop);
        tapex = apex(tetloop);
        setsorg(subloop, torg);
        setsdest(subloop, tdest);
        setsapex(subloop, tapex);
        // Set the vertices be FREESUBVERTEX to indicate they belong to a
        //   facet of the domain.  They may be changed later.
        setpointtype(torg, FREESUBVERTEX);
        setpointtype(tdest, FREESUBVERTEX);
        setpointtype(tapex, FREESUBVERTEX);
        tsbond(tetloop, subloop);
        if (neightet.tet != dummytet) {
          sesymself(subloop);
          tsbond(neightet, subloop);
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Set the connection between subfaces. A subsegment may have more than
  //   two subfaces sharing it, 'neighshlist' stores all subfaces sharing
  //   one edge.
  neighshlist = new list(sizeof(face), NULL);
  // Create a map from nodes to subfaces.
  makesubfacemap(idx2facelist, facesperverlist);

  // Loop over the set of subfaces, setup the connection between subfaces.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      spivot(subloop, neighsh);
      if (neighsh.sh == dummysh) {
        // This side is 'empty', operate on it.
        torg = sorg(subloop);
        tdest = sdest(subloop);
        tapex = sapex(subloop);
        neighshlist->append(&subloop);
        iorg = pointmark(torg) - in->firstnumber;
        // Search its neighbor in the adjacent list of torg.
        for (j = idx2facelist[iorg]; j < idx2facelist[iorg + 1]; j++) {
          neighsh.sh = facesperverlist[j];
          if (neighsh.sh == subloop.sh) continue;
          neighsh.shver = 0;
          if (isfacehasedge(&neighsh, torg, tdest)) {
            findedge(&neighsh, torg, tdest);
            // Insert 'neighsh' into 'neighshlist'.
            if (neighshlist->len() < 2) {
              neighshlist->append(&neighsh);
            } else {
              for (index = 0; index < neighshlist->len() - 1; index++) {
                sface1 = * (face *)(* neighshlist)[index];
                sface2 = * (face *)(* neighshlist)[index + 1];
                da1 = facedihedral(torg, tdest, sapex(sface1), sapex(neighsh));
                da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2));
                if (da1 < da2) {
                  break;  // Insert it after index.
                }
              }
              neighshlist->insert(index + 1, &neighsh);
            }
          }
        }
        // Bond the subfaces in 'neighshlist'. 
        if (neighshlist->len() > 1) {
          neighsh = * (face *)(* neighshlist)[0];
          for (j = 1; j <= neighshlist->len(); j++) {
            if (j < neighshlist->len()) {
              neineighsh = * (face *)(* neighshlist)[j];
            } else {
              neineighsh = * (face *)(* neighshlist)[0];
            }
            sbond1(neighsh, neineighsh);
            neighsh = neineighsh;
          }
        } else {
          // No neighbor subface be found, bond 'subloop' to itself.
          sbond(subloop, subloop);
        }
        neighshlist->clear();
      }
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }

  // Segments will be introudced. Each segment has a unique marker (1-based).
  marker = 1;
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      sspivot(subloop, subseg);
      if (subseg.sh == dummysh) {
        // This side has no subsegment bonded, check it.
        torg = sorg(subloop);
        tdest = sdest(subloop);
        tapex = sapex(subloop);
        spivot(subloop, neighsh);
        spivot(neighsh, neineighsh);
        insertsegflag = false;
        if (subloop.sh == neighsh.sh || subloop.sh != neineighsh.sh) {
          // This side is either self-bonded or more than two subfaces,
          //   insert a subsegment at this side.
          insertsegflag = true;
        } else {
          // Only two subfaces case.
#ifdef SELF_CHECK
          assert(subloop.sh != neighsh.sh);
#endif
          napex = sapex(neighsh);
          sign = orient3d(torg, tdest, tapex, napex);
          if (iscoplanar(torg, tdest, tapex, napex, sign, b->epsilon)) {
            // Although they are coplanar, we still need to check if they
            //   have the same boundary marker.
            insertsegflag = (shellmark(subloop) != shellmark(neighsh));
          } else {
            // Non-coplanar.
            insertsegflag = true;
          }
        }
        if (insertsegflag) {
          // Create a subsegment at this side.
          makeshellface(subsegs, &subseg);
          setsorg(subseg, torg);
          setsdest(subseg, tdest);
          // The two vertices have been marked as FREESUBVERTEX. Now mark
          //   them as NACUTEVERTEX.
          setpointtype(torg, NACUTEVERTEX);
          setpointtype(tdest, NACUTEVERTEX);
          setshellmark(subseg, marker);
          marker++;
          // Bond all subfaces to this subsegment.
          neighsh = subloop;
          do {
            ssbond(neighsh, subseg);
            spivotself(neighsh);
          } while (neighsh.sh != subloop.sh);
        }
      }
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }
  // Remember the number of input segments.
  insegments = subsegs->items;
  // Find the acute vertices and set them be type ACUTEVERTEX.

  // Indentify facets and set the facet marker (1-based) for subfaces.
  markerlist = new list("int");
  
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    // Only operate on uninfected subface, after operating, infect it.
    if (!sinfected(subloop)) {
      // A new facet is found.
      marker = shellmark(subloop);
      markerlist->append(&marker);
      facetidx = markerlist->len(); // 'facetidx' starts from 1.
      setshellmark(subloop, facetidx);
      sinfect(subloop);
      neighshlist->append(&subloop);
      // Find out all subfaces of this facet (bounded by subsegments).
      for (i = 0; i < neighshlist->len(); i++) {
        neighsh = * (face *) (* neighshlist)[i];
        for (j = 0; j < 3; j++) {
          sspivot(neighsh, subseg);
          if (subseg.sh == dummysh) {
            spivot(neighsh, neineighsh);
            if (!sinfected(neineighsh)) {
              // 'neineighsh' is in the same facet as 'subloop'.
#ifdef SELF_CHECK
              assert(shellmark(neineighsh) == marker);
#endif
              setshellmark(neineighsh, facetidx);
              sinfect(neineighsh);
              neighshlist->append(&neineighsh);
            }
          }
          senextself(neighsh);
        }
      }
      neighshlist->clear();
    }
    subloop.sh = shellfacetraverse(subfaces);
  }
  // Uninfect all subfaces.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
#ifdef SELF_CHECK
    assert(sinfected(subloop));
#endif
    suninfect(subloop);
    subloop.sh = shellfacetraverse(subfaces);
  }
  // Save the facet markers in 'in->facetmarkerlist'.
  in->numberoffacets = markerlist->len();
  in->facetmarkerlist = new int[in->numberoffacets];
  for (i = 0; i < in->numberoffacets; i++) {
    marker = * (int *) (* markerlist)[i];
    in->facetmarkerlist[i] = marker;
  }
  // Initialize the 'facetabovepointlist'.
  facetabovepointarray = new point[in->numberoffacets + 1];
  for (i = 0; i < in->numberoffacets + 1; i++) {
    facetabovepointarray[i] = (point) NULL;
  }

  // The mesh contains boundary now.
  checksubfaces = 1;
  // The mesh is nonconvex now.
  nonconvex = 1;

  // Is there periodic boundary confitions?
  if (checkpbcs) {
    tetgenio::pbcgroup *pg;
    pbcdata *pd;
    // Initialize the global array 'subpbcgrouptable'.
    createsubpbcgrouptable();
    // Loop for each pbcgroup i.
    for (i = 0; i < in->numberofpbcgroups; i++) {
      pg = &(in->pbcgrouplist[i]);
      pd = &(subpbcgrouptable[i]);
      // Find all subfaces of pd, set each subface's group id be i.
      for (j = 0; j < 2; j++) {
        subfaces->traversalinit();
        subloop.sh = shellfacetraverse(subfaces);
        while (subloop.sh != (shellface *) NULL) {
          facetidx = shellmark(subloop);
          marker = in->facetmarkerlist[facetidx - 1];
          if (marker == pd->fmark[j]) {
            setshellpbcgroup(subloop, i);
            pd->ss[j] = subloop;
          }
          subloop.sh = shellfacetraverse(subfaces);
        }
      }
      if (pg->pointpairlist != (int *) NULL) {
        // Set the connections between pbc point pairs.
        for (j = 0; j < pg->numberofpointpairs; j++) {
          iorg = pg->pointpairlist[j * 2] - in->firstnumber;
          idest = pg->pointpairlist[j * 2 + 1] - in->firstnumber;
          torg = idx2verlist[iorg];
          tdest = idx2verlist[idest];
          setpoint2pbcpt(torg, tdest);
          setpoint2pbcpt(tdest, torg);
        }
      }
    }
    // Create the global array 'segpbcgrouptable'.
    createsegpbcgrouptable();
  }

  delete markerlist;
  delete neighshlist;
  delete [] worklist;
  delete [] idx2tetlist;
  delete [] tetsperverlist;
  delete [] idx2facelist;
  delete [] facesperverlist;
  delete [] idx2verlist;
  
  return hullsize;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertconstrainedpoints()    Insert a list of constrained points.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertconstrainedpoints(tetgenio *addio)
{
  queue *flipqueue;
  triface searchtet;
  face checksh, checkseg;
  point newpoint;
  enum locateresult loc;
  REAL *attr;
  bool insertflag;
  int covertices, outvertices;
  int index;
  int i, j;
  
  if (!b->quiet) {
    printf("Insert additional points into mesh.\n");
  }
  // Initialize 'flipqueue'.
  flipqueue = new queue(sizeof(badface));
  recenttet.tet = dummytet;
  covertices = outvertices = 0;

  index = 0;
  for (i = 0; i < addio->numberofpoints; i++) {
    // Create a newpoint.
    makepoint(&newpoint);
    newpoint[0] = addio->pointlist[index++];
    newpoint[1] = addio->pointlist[index++];
    newpoint[2] = addio->pointlist[index++];
    // Read the add point attributes if current points have attributes.
    if ((addio->numberofpointattributes > 0) &&
        (in->numberofpointattributes > 0)) {
      attr = addio->pointattributelist + addio->numberofpointattributes * i;
      for (j = 0; j < in->numberofpointattributes; j++) {
        if (j < addio->numberofpointattributes) {
          newpoint[3 + j] = attr[j];
        }
      }
    }
    // Find the location of the inserted point.
    searchtet = recenttet;
    loc = locate(newpoint, &searchtet);
    if (loc != ONVERTEX) {
      loc = adjustlocate(newpoint, &searchtet, loc, b->epsilon2);
    }
    if (loc == OUTSIDE) {
      loc = hullwalk(newpoint, &searchtet);
      if (loc == OUTSIDE) {
        // Perform a brute-force search.
        tetrahedrons->traversalinit();
        searchtet.tet = tetrahedrontraverse();
        while (searchtet.tet != (tetrahedron *) NULL) {
          loc = adjustlocate(newpoint, &searchtet, OUTSIDE, b->epsilon2);
          if (loc != OUTSIDE) break;
          searchtet.tet = tetrahedrontraverse();
        }
      }
    }
    // Insert the point if it not lies outside or on a vertex.
    insertflag = true;
    switch (loc) {
    case INTETRAHEDRON:
      setpointtype(newpoint, FREEVOLVERTEX);
      splittetrahedron(newpoint, &searchtet, flipqueue);
      break;
    case ONFACE:
      tspivot(searchtet, checksh);
      if (checksh.sh != dummysh) {
        // It is a boundary face. Don't insert it if -Y option is used.
        if (b->nobisect) {
          insertflag = false;
        } else {
          setpointtype(newpoint, FREESUBVERTEX);
        }
      } else {
        setpointtype(newpoint, FREEVOLVERTEX);
      }
      if (insertflag) {
        splittetface(newpoint, &searchtet, flipqueue);
      }
      break;
    case ONEDGE:
      tsspivot(&searchtet, &checkseg);
      if (checkseg.sh != dummysh) {
        if (b->nobisect) {
          insertflag = false;
        } else {
          setpointtype(newpoint, FREESEGVERTEX);
          setpoint2sh(newpoint, sencode(checkseg));
        }
      } else {
        tspivot(searchtet, checksh);
        if (checksh.sh != dummysh) {
          if (b->nobisect) {
            insertflag = false;
          } else {
            setpointtype(newpoint, FREESUBVERTEX);
          }
        } else {
          setpointtype(newpoint, FREEVOLVERTEX);
        }
      }
      if (insertflag) {
        splittetedge(newpoint, &searchtet, flipqueue);
      }
      break;
    case ONVERTEX:
      insertflag = false;
      covertices++;
      break;
    case OUTSIDE:
      insertflag = false;
      outvertices++;
      break;
    }
    // Remember the tetrahedron for next point searching.
    recenttet = searchtet;
    if (!insertflag) {
      pointdealloc(newpoint);
    } else {
      flip(flipqueue, NULL);
    }
  }

  if (b->verbose) {
    if (covertices > 0) {
      printf("  %d constrained points already exist.\n", covertices);
    }
    if (outvertices > 0) {
      printf("  %d constrained points lie outside the mesh.\n", outvertices);
    }
    printf("  %d constrained points have been inserted.\n", 
           addio->numberofpoints - covertices - outvertices);
  }

  delete flipqueue;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// p1interpolatebgm()    Set pt size by p^1 interpolation in background mesh.//
//                                                                           //
// On input, 'bgmtet' is a suggesting tet in background mesh for searching   //
// 'pt'. It returns the tet containing 'pt'.                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::p1interpolatebgm(point pt, triface* bgmtet, long *scount)
{
  point bgmpt[4];
  enum locateresult loc;
  REAL vol, volpt[4], weights[4];
  int i;

  loc = bgm->preciselocate(pt, bgmtet, bgm->tetrahedrons->items);
  if (loc == OUTSIDE) {
    loc = bgm->hullwalk(pt, bgmtet);
    if (loc == OUTSIDE) {
      // Perform a brute-force search.
      if (b->verbose) {
        printf("Warning:  Global point location.\n");
      }
      if (scount) (*scount)++;
      bgm->tetrahedrons->traversalinit(); // in bgm
      bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm
      while (bgmtet->tet != (tetrahedron *) NULL) {
        loc = bgm->adjustlocate(pt, bgmtet, OUTSIDE, b->epsilon);
        if (loc != OUTSIDE) break;
        bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm
      }
    }
  }
  if (loc != OUTSIDE) {
    // Let p remember t.
    setpoint2bgmtet(pt, encode(*bgmtet)); // in m
    // get the corners of t.
    for (i = 0; i < 4; i++) bgmpt[i] = (point) bgmtet->tet[4 + i];
    // Calculate the weighted coordinates of p in t.
    vol = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], bgmpt[3]);
    volpt[0] = orient3d(pt, bgmpt[1], bgmpt[2], bgmpt[3]);
    volpt[1] = orient3d(bgmpt[0], pt, bgmpt[2], bgmpt[3]);
    volpt[2] = orient3d(bgmpt[0], bgmpt[1], pt, bgmpt[3]);
    volpt[3] = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], pt);
    for (i = 0; i < 4; i++) weights[i] = fabs(volpt[i] / vol);
    // Interpolate the solution for p.
    for (i = 0; i < bgm->in->numberofpointmtrs; i++) {
      pt[pointmtrindex + i] = weights[0] * bgmpt[0][bgm->pointmtrindex + i]
                            + weights[1] * bgmpt[1][bgm->pointmtrindex + i]
                            + weights[2] * bgmpt[2][bgm->pointmtrindex + i]
                            + weights[3] * bgmpt[3][bgm->pointmtrindex + i];
    }
  } else {
    setpoint2bgmtet(pt, (tetrahedron) NULL);  // in m
  }
  return loc != OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interpolatesizemap()    Interpolate the point sizes in the given size map.//
//                                                                           //
// The size map is specified on each node of the background mesh. The points //
// of current mesh get their sizes by interpolating.                         //
//                                                                           //
// This function operation on two meshes simultaneously, the current mesh m, //
// and the background mesh bgm. After this function, each point p in m will  //
// have a pointer to a tet of bgm.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::interpolatesizemap()
{
  list *adjtetlist;
  triface tetloop, neightet, bgmtet;
  point searchpt;
  long scount;
  int *worklist;
  int sepcount;
  int i;

  if (b->verbose) {
    printf("  Interpolating size map.\n");
  }

  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 0;
  sepcount = 0;
  scount = 0l;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      // Find a new subdomain.
      adjtetlist = new list(sizeof(triface), NULL, 1024);
      infect(tetloop);
      // Search the four corners in background mesh.
      for (i = 0; i < 4; i++) {
        searchpt = (point) tetloop.tet[4 + i];
        // Mark the point for avoiding multiple searchings.
        // assert(worklist[pointmark(searchpt)] == 0);
        worklist[pointmark(searchpt)] = 1;
        // Does it contain a pointer to bgm tet?
        bgm->decode(point2bgmtet(searchpt), bgmtet);
        if (bgm->isdead(&bgmtet)) {
          bgmtet = bgm->recenttet;
        }
        if (p1interpolatebgm(searchpt, &bgmtet, &scount)) {
          bgm->recenttet = bgmtet;
        }
      } // for (i = 0; i < 4; i++)
      // Collect all tets in this region.
      adjtetlist->append(&tetloop);
      // Collect the tets in the subdomain.
      for (i = 0; i < adjtetlist->len(); i++) {
        tetloop = * (triface *)(* adjtetlist)[i];
        for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
          sym(tetloop, neightet);
          if ((neightet.tet != dummytet) && !infected(neightet)) {
            // Only need to search for the opposite point.
            searchpt = oppo(neightet);
            if (worklist[pointmark(searchpt)] == 0) {
              worklist[pointmark(searchpt)] = 1;
              decode(point2bgmtet(searchpt), bgmtet);
              if (bgm->isdead(&bgmtet)) {
                bgmtet = bgm->recenttet;
              }
              if (p1interpolatebgm(searchpt, &bgmtet, &scount)) {
                bgm->recenttet = bgmtet;
              }
            }
            infect(neightet);
            adjtetlist->append(&neightet);
          }
        }
      }
      // Increase the number of separated domains.
      sepcount++;
      delete adjtetlist;
    } // if (!infect())
    tetloop.tet = tetrahedrontraverse();
  }

  // Unmark all tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    assert(infected(tetloop));
    uninfect(tetloop);
    tetloop.tet = tetrahedrontraverse();
  }
  delete [] worklist;

#ifdef SELF_CHECK
  if (b->verbose && scount > 0l) {
    printf("  %ld brute-force searches.\n", scount);
  }
  if (b->verbose && sepcount > 0) {
    printf("  %d separate domains.\n", sepcount);
  }
#endif
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// duplicatebgmesh()    Duplicate current mesh to background mesh.           //
//                                                                           //
// Current mesh 'this' is copied into 'this->bgm'.Both meshes share the same //
// input tetgenio object, 'this->in', same tetgenbehavior object 'this->b'.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::duplicatebgmesh()
{
  triface tetloop, btetloop;
  triface symtet, bsymtet;
  face bhullsh, bneighsh;
  point *idx2bplist, *tetptbaklist;
  point ploop, bploop;
  int idx, i;

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

  // The background mesh itself has no background mesh.
  // assert(bgm->bgm == (tetgenmesh *) NULL);
  // The space for metric tensor should be allocated.
  // assert(bgm->sizeoftensor > 0);

  // Copy point list.
  idx2bplist = new point[points->items + 1];
  idx = in->firstnumber;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) NULL) {
    bgm->makepoint(&bploop);
    // Copy coordinates, attributes.
    for (i = 0; i < 3 + in->numberofpointattributes; i++) {
      bploop[i] = ploop[i];
    }
    // Transfer the metric tensor.
    for (i = 0; i < bgm->sizeoftensor; i++) {
      bploop[bgm->pointmtrindex + i] = ploop[pointmtrindex + i];
      // Metric tensor should have a positive value.
      if (bploop[bgm->pointmtrindex + i] <= 0.0) {
        printf("Error:  Point %d has non-positive size %g (-m option).\n",
               bgm->pointmark(bploop), bploop[bgm->pointmtrindex + i]);
        terminatetetgen(1);
      }
    }
    // Remember the point for searching.
    idx2bplist[idx++] = bploop; 
    ploop = pointtraverse();
  }

  // Copy tetrahedra list.
  tetptbaklist = new point[tetrahedrons->items + 1];
  idx = in->firstnumber;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    bgm->maketetrahedron(&btetloop);
    // Set the four corners.
    for (i = 0; i < 4; i++) {
      ploop = (point) tetloop.tet[4 + i];
      bploop = idx2bplist[pointmark(ploop)];
      btetloop.tet[4 + i] = (tetrahedron) bploop;
    }
    // Remember the tet for setting neighbor connections.
    tetptbaklist[idx++] = (point) tetloop.tet[4];
    tetloop.tet[4] = (tetrahedron) btetloop.tet; 
    tetloop.tet = tetrahedrontraverse();
  }

  // Set the connections between background tetrahedra. Create background
  //   hull subfaces. Create the map of point-to-bgmtet. 
  idx = in->firstnumber;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Get the corresponding background tet.
    btetloop.tet = (tetrahedron *) tetloop.tet[4];
    // Set the four neighbors.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      btetloop.loc = tetloop.loc;
      sym(tetloop, symtet);
      if ((symtet.tet != dummytet) && (symtet.tet > tetloop.tet)) {
        // Operate on the un-connected interior face.
        bsymtet.tet = (tetrahedron *) symtet.tet[4]; // The saved bgm tet.
        bsymtet.loc = symtet.loc;
        bgm->bond(btetloop, bsymtet);
      } else if (symtet.tet == dummytet) {
        // Create a subface in background mesh.
        bgm->makeshellface(bgm->subfaces, &bhullsh);
        bgm->adjustedgering(btetloop, CCW); // face to inside.
        bgm->setsorg(bhullsh, bgm->org(btetloop));
        bgm->setsdest(bhullsh, bgm->dest(btetloop));
        bgm->setsapex(bhullsh, bgm->apex(btetloop));
        bgm->tsbond(btetloop, bhullsh);
        // Remember a hull face for point location.
        bgm->dummytet[0] = bgm->encode(btetloop);
      }
    }
    // Restore the backup tet point.
    tetloop.tet[4] = (tetrahedron) tetptbaklist[idx++];
    // Make the point-to-bgmtet map for size interpolation.
    btetloop.loc = 0;
    for (i = 0; i < 4; i++) {
      ploop = (point) tetloop.tet[4 + i];
      setpoint2bgmtet(ploop, bgm->encode(btetloop));
    } 
    // Go to the next tet, btet.
    tetloop.tet = tetrahedrontraverse();
  }

  // Connect bgm hull subfaces. Note: all hull subfaces form a 2-manifold.
  bgm->subfaces->traversalinit();
  bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces);
  while (bhullsh.sh != (shellface *) NULL) {
    bhullsh.shver = 0;
    bgm->stpivot(bhullsh, btetloop);
    assert(btetloop.tet != bgm->dummytet);
    bgm->adjustedgering(btetloop, CCW);
    for (i = 0; i < 3; i++) {
      bgm->spivot(bhullsh, bneighsh);
      if (bneighsh.sh == bgm->dummysh) {
        // This side is open, operate on it.
        bsymtet = btetloop;
        while (bgm->fnextself(bsymtet));
        bgm->tspivot(bsymtet, bneighsh);
        bgm->findedge(&bneighsh, bgm->sdest(bhullsh), bgm->sorg(bhullsh));
        bgm->sbond(bhullsh, bneighsh);
      }
      bgm->enextself(btetloop);
      bgm->senextself(bhullsh);
    }
    bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces);
  }

  delete [] tetptbaklist;
  delete [] idx2bplist;
}

//
// Begin of Delaunay refinement routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// marksharpsegments()    Mark sharp segments.                               //
//                                                                           //
// A segment s is called sharp if it is in one of the two cases:             //
//  (1) There is a segment s' intersecting with s.  The internal angle (*)   //
//      between s and s' is acute.                                           //
//  (2) There are two facets f1 and f2 intersecting at s.  The internal      //
//      dihedral angle (*) between f1 and f2 is acute.                       //
// This routine finds the sharp segments and marked them as type SHARP.      //
// The minimum angle between segments (minfaceang) and the minimum dihedral  //
// angle between facets (minfacetdihed) are calulcated.                      //
//                                                                           //
// (*) The internal angle (or dihedral) bewteen two features means the angle //
// inside the mesh domain.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::marksharpsegments(REAL sharpangle)
{
  triface adjtet;
  face startsh, spinsh, neighsh;
  face segloop, prevseg, nextseg;
  point eorg, edest;
  REAL ang, smallang;
  bool issharp;
  int sharpsegcount;

  if (b->verbose > 0) {
    printf("  Marking sharp segments.\n");
  }

  smallang = sharpangle * PI / 180.;
  sharpsegcount = 0;
  eorg = edest = (point) NULL; // To avoid compiler warnings.
   
  // A segment s may have been split into many subsegments. Operate the one
  //   which contains the origin of s. Then mark the rest of subsegments.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    segloop.shver = 0;
    senext2(segloop, prevseg);
    spivotself(prevseg);
    if (prevseg.sh == dummysh) {
      // Operate on this seg s.
      assert(shelltype(segloop) != SHARP); // It should be unmarked.
      issharp = false;
      spivot(segloop, startsh);
      if (startsh.sh != dummysh) {
        // First check if two facets form an acute dihedral angle at s.
        eorg = sorg(segloop);
        edest = sdest(segloop);
        spinsh = startsh;
        do {
          if (sorg(spinsh) != eorg) {
            sesymself(spinsh);
          }
          // Only do test when the spinsh is faceing inward.
          stpivot(spinsh, adjtet);          
          if (adjtet.tet != dummytet) {
            // Get the subface on the adjacent facet.
            spivot(spinsh, neighsh);
            // Do not calculate if it is self-bonded.
            if (neighsh.sh != spinsh.sh) {
              // Calculate the dihedral angle between the two subfaces.
              ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh));
              // Only do check if a sharp angle has not been found.
              if (!issharp) issharp = (ang < smallang);
              // Remember the smallest facet dihedral angle.
              minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang;
            }
          }
          // Go to the next facet.
          spivotself(spinsh);
        } while (spinsh.sh != startsh.sh);
        // if (!issharp) {
          // Second check if s forms an acute angle with another seg.
          spinsh = startsh;
          do {
            if (sorg(spinsh) != eorg) {
              sesymself(spinsh);
            }
            // Calculate the angle between s and s' of this facet.
            neighsh = spinsh;
            // Rotate edges around 'eorg' until meeting another seg s'. Such  
            //   seg (s') must exist since the facet is segment-bounded.
            //   The sum of the angles of faces at 'eorg' gives the internal
            //   angle between the two segments.
            ang = 0.0;
            do {
              ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL);
              senext2self(neighsh);
              sspivot(neighsh, nextseg);
              if (nextseg.sh != dummysh) break;
              // Go to the next coplanar subface.
              spivotself(neighsh);
              assert(neighsh.sh != dummysh);
              if (sorg(neighsh) != eorg) {
                sesymself(neighsh);
              }
            } while (true);
            // Only do check if a sharp angle has not been found.
            if (!issharp) issharp = (ang < smallang);
            // Remember the smallest input face angle.
            minfaceang = minfaceang < ang ? minfaceang : ang;
            // Go to the next facet.
            spivotself(spinsh);
          } while (spinsh.sh != startsh.sh);
        // }
      }
      if (issharp) {
        setshelltype(segloop, SHARP);
        // Set the type for all subsegments at forwards.
        senext(segloop, nextseg);
        spivotself(nextseg);
        while (nextseg.sh != dummysh) {
          nextseg.shver = 0;
          setshelltype(nextseg, SHARP);
          senextself(nextseg);
          spivotself(nextseg);
        }
        sharpsegcount++;
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // So far we have marked all segments which have an acute dihedral angle
  //   or whose ORIGINs have an acute angle. In the un-marked subsegments,
  //   there are possible ones whose DESTINATIONs have an acute angle.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Only operate if s is non-sharp and contains the dest.
    segloop.shver = 0;
    senext(segloop, nextseg);
    spivotself(nextseg);
    // if ((nextseg.sh == dummysh) && (shelltype(segloop) != SHARP)) {
    if (nextseg.sh == dummysh) {
      // issharp = false;
      issharp = (shelltype(segloop) == SHARP);
      spivot(segloop, startsh);
      if (startsh.sh != dummysh) {
        // Check if s forms an acute angle with another seg.
        eorg = sdest(segloop);
        spinsh = startsh;
        do {
          if (sorg(spinsh) != eorg) {
            sesymself(spinsh);
          }
          // Calculate the angle between s and s' of this facet.
          neighsh = spinsh;
          ang = 0.0;
          do {
            ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL);
            senext2self(neighsh);
            sspivot(neighsh, nextseg);
            if (nextseg.sh != dummysh) break;
            // Go to the next coplanar subface.
            spivotself(neighsh);
            assert(neighsh.sh != dummysh);
            if (sorg(neighsh) != eorg) {
              sesymself(neighsh);
            }
          } while (true);
          // Only do check if a sharp angle has not been found.
          if (!issharp) issharp = (ang < smallang);
          // Remember the smallest input face angle.
          minfaceang = minfaceang < ang ? minfaceang : ang;
          // Go to the next facet.
          spivotself(spinsh);
        } while (spinsh.sh != startsh.sh);
      }
      if (issharp) {
        setshelltype(segloop, SHARP);
        // Set the type for all subsegments at backwards.
        senext2(segloop, prevseg);
        spivotself(prevseg);
        while (prevseg.sh != dummysh) {
          prevseg.shver = 0;
          setshelltype(prevseg, SHARP);
          senext2self(prevseg);
          spivotself(prevseg);
        }
        sharpsegcount++;
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  } 

  if ((b->verbose > 0) && (sharpsegcount > 0)) {
    printf("  %d sharp segments.\n", sharpsegcount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// decidefeaturepointsizes()    Decide the sizes for all feature points.     //
//                                                                           //
// A feature point is a point on a sharp segment. Every feature point p will //
// be assigned a positive size which is the radius of the protecting ball.   //
//                                                                           //
// The size of a feature point may be specified by one of the following ways://
//   (1) directly specifying on an input vertex (by using .mtr file);        //
//   (2) imposing a fixed maximal volume constraint ('-a__' option);         //
//   (3) imposing a maximal volume constraint in a region ('-a' option);     //
//   (4) imposing a maximal area constraint on a facet (in .var file);       //
//   (5) imposing a maximal length constraint on a segment (in .var file);   //
//   (6) combining (1) - (5).                                                //
//   (7) automatically deriving a size if none of (1) - (6) is available.    //
// In case (7),the size of p is set to be the smallest edge length among all //
// edges connecting at p.  The final size of p is the minimum of (1) - (7).  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::decidefeaturepointsizes()
{
  list *tetlist, *verlist;
  shellface **segsperverlist;
  triface starttet;
  face shloop;
  face checkseg, prevseg, nextseg, testseg;
  point ploop, adjpt, e1, e2;
  REAL lfs_0, len, vol, maxlen, varlen;
  bool isfeature;
  int *idx2seglist;
  int featurecount;
  int idx, i, j;

  if (b->verbose > 0) {
    printf("  Deciding feature-point sizes.\n");
  }

  // Constructing a map from vertices to segments.
  makesegmentmap(idx2seglist, segsperverlist);
  // Initialize working lists.
  tetlist = new list(sizeof(triface), NULL, 256);
  verlist = new list(sizeof(point *), NULL, 256);

  if (b->fixedvolume) {
    // A fixed volume constraint is imposed. This gives an upper bound of
    //   the maximal radius of the protect ball of a vertex.
    maxlen = pow(6.0 * b->maxvolume, 1.0/3.0);
  }

  if (!b->refine) {
    // Initially correct types for Steiner points.
    featurecount = 0;
    points->traversalinit();
    ploop = pointtraverse();
    while (ploop != (point) NULL) {
      if (pointtype(ploop) == NACUTEVERTEX) {
        if (point2sh(ploop) != (shellface) NULL) {
          setpointtype(ploop, FREESEGVERTEX);
          featurecount++;
        }
      }
      ploop = pointtraverse();
    }
#ifdef SELF_CHECK
    if ((b->verbose > 0) && (featurecount > 0)) {
      printf("  %d Steiner points correction.\n", featurecount);
    }
#endif
  }

  // First only assign a size of p if p is not a Steiner point. The size of
  //   a Steiner point will be interpolated later from the endpoints of the
  //   segment on which it lies. 
  featurecount = 0;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) NULL) {
    if (pointtype(ploop) != FREESEGVERTEX) {
      // Is p a feature point?
      isfeature = false;
      idx = pointmark(ploop) - in->firstnumber;
      for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; i++) {
        checkseg.sh = segsperverlist[i];
        isfeature = (shelltype(checkseg) == SHARP);
      }
      // Decide the size of p if it is on a sharp segment.
      if (isfeature) {
        // Find a tet containing p (checkseg is a sharp seg which contains p).
        sstpivot(&checkseg, &starttet);
        // Form star(p).
        tetlist->append(&starttet);
        formstarpolyhedron(ploop, tetlist, verlist, true);
        // Decide the size for p if no input size is given on input.
        if (ploop[pointmtrindex] == 0.0) {
          // Calculate lfs_0(p).
          lfs_0 = longest;
          for (i = 0; i < verlist->len(); i++) {
            adjpt = * (point *)(* verlist)[i];
            if (pointtype(adjpt) == FREESEGVERTEX) {
              // A Steiner point q. Find the seg it lies on.
              sdecode(point2sh(adjpt), checkseg);
              assert(checkseg.sh != dummysh);
              checkseg.shver = 0;
              // Find the origin of this seg.
              prevseg = checkseg;
              do {
                senext2(prevseg, testseg);
                spivotself(testseg);
                if (testseg.sh == dummysh) break;
                prevseg = testseg; // Go to the previous subseg.
                prevseg.shver = 0;
              } while (true);
              // Find the dest of this seg.
              nextseg = checkseg;
              do {
                senext(nextseg, testseg);
                spivotself(testseg);
                if (testseg.sh == dummysh) break;
                nextseg = testseg; // Go to the next subseg.
                nextseg.shver = 0;
              } while (true);
              e1 = sorg(prevseg);
              e2 = sdest(nextseg);
              // Check if p is the origin or the dest of this seg.
              if (ploop == e1) {
                // Set q to be the dest of this seg.
                adjpt = e2;
              } else if (ploop == e2) {
                // Set q to be the org of this seg.
                adjpt = e1;
              }
            }
            len = distance(ploop, adjpt);
            if (lfs_0 > len) lfs_0 = len;
          }
          ploop[pointmtrindex] = lfs_0;
        }
        if (b->fixedvolume) {
          // A fixed volume constraint is imposed. Adjust H(p) <= maxlen.
          if (ploop[pointmtrindex] > maxlen) {
            ploop[pointmtrindex] = maxlen;
          }
        }
        if (b->varvolume) {
          // Variant volume constraints are imposed. Adjust H(p) <= varlen.
          for (i = 0; i < tetlist->len(); i++) {
            starttet = * (triface *)(* tetlist)[i];
            vol = volumebound(starttet.tet);
            if (vol > 0.0) {
              varlen = pow(6 * vol, 1.0/3.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          }
        }
        // Clear working lists.
        tetlist->clear();
        verlist->clear();
        featurecount++;
      } else {
        // NO feature point, set the size of p be zero.
        ploop[pointmtrindex] = 0.0;
      }
    } // if (pointtype(ploop) != FREESEGVERTEX) {
    ploop = pointtraverse();
  }

  if (b->verbose > 0) {
    printf("  %d feature points.\n", featurecount);
  }

  if (!b->refine) {
    // Second only assign sizes for all Steiner points. A Steiner point p
    //   inserted on a sharp segment s is assigned a size by interpolating
    //   the sizes of the original endpoints of s.
    featurecount = 0;
    points->traversalinit();
    ploop = pointtraverse();
    while (ploop != (point) NULL) {
      if (pointtype(ploop) == FREESEGVERTEX) {
        if (ploop[pointmtrindex] == 0.0) {
          sdecode(point2sh(ploop), checkseg);
          assert(checkseg.sh != dummysh);
          if (shelltype(checkseg) == SHARP) {
            checkseg.shver = 0;
            // Find the origin of this seg.
            prevseg = checkseg;
            do {
              senext2(prevseg, testseg);
              spivotself(testseg);
              if (testseg.sh == dummysh) break;
              prevseg = testseg; // Go the previous subseg.
              prevseg.shver = 0;
            } while (true);
            // Find the dest of this seg.
            nextseg = checkseg;
            do {
              senext(nextseg, testseg);
              spivotself(testseg);
              if (testseg.sh == dummysh) break;
              nextseg = testseg; // Go the next subseg.
              nextseg.shver = 0;
            } while (true);
            e1 = sorg(prevseg);
            e2 = sdest(nextseg);
            len = distance(e1, e2);
            lfs_0 = distance(e1, ploop);
            // The following assert() happens when -Y option is used.
            if (b->nobisect == 0) {
              assert(lfs_0 < len); 
            }
            ploop[pointmtrindex] = e1[pointmtrindex]
              + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]);
            featurecount++;
          } else {
            // NO feature point, set the size of p be zero.
            ploop[pointmtrindex] = 0.0;
          } // if (shelltype(checkseg) == SHARP)
        } // if (ploop[pointmtrindex] == 0.0)
      } // if (pointtype(ploop) != FREESEGVERTEX)
      ploop = pointtraverse();
    }
    if ((b->verbose > 0) && (featurecount > 0)) {
      printf("  %d Steiner feature points.\n", featurecount);
    }
  }

  if (varconstraint) {
    // A .var file exists. Adjust feature sizes.
    if (in->facetconstraintlist) {
      // Have facet area constrains.
      subfaces->traversalinit();
      shloop.sh = shellfacetraverse(subfaces);
      while (shloop.sh != (shellface *) NULL) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the three corners are feature points.
          varlen = sqrt(varlen);
          for (j = 0; j < 3; j++) {
            ploop = (point) shloop.sh[3 + j];
            isfeature = false;
            idx = pointmark(ploop) - in->firstnumber;
            for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; 
                 i++) {
              checkseg.sh = segsperverlist[i];
              isfeature = (shelltype(checkseg) == SHARP);
            }
            if (isfeature) {
              assert(ploop[pointmtrindex] > 0.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          } // for (j = 0; j < 3; j++)
        }
        shloop.sh = shellfacetraverse(subfaces);
      }
    }
    if (in->segmentconstraintlist) {
      // Have facet area constrains.
      subsegs->traversalinit();
      shloop.sh = shellfacetraverse(subsegs);
      while (shloop.sh != (shellface *) NULL) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the two endpoints are feature points.
          for (j = 0; j < 2; j++) {
            ploop = (point) shloop.sh[3 + j];
            isfeature = false;
            idx = pointmark(ploop) - in->firstnumber;
            for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; 
                 i++) {
              checkseg.sh = segsperverlist[i];
              isfeature = (shelltype(checkseg) == SHARP);
            }
            if (isfeature) {
              assert(ploop[pointmtrindex] > 0.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          } // for (j = 0; j < 2; j++)
        }
        shloop.sh = shellfacetraverse(subsegs);
      }
    }
  } // if (varconstraint)

  delete [] segsperverlist;
  delete [] idx2seglist;
  delete tetlist;
  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueueencsub()    Add an encroached subface into the queue.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueueencsub(face* testsub, point encpt, int quenumber,
  REAL* cent)
{
  badface *encsub;
  int i;

  encsub = (badface *) badsubfaces->alloc();
  encsub->ss = *testsub;
  encsub->forg = sorg(*testsub);
  encsub->fdest = sdest(*testsub);
  encsub->fapex = sapex(*testsub);
  encsub->foppo = (point) encpt;
  for (i = 0; i < 3; i++) encsub->cent[i] = cent[i];
  encsub->nextitem = (badface *) NULL;
  // Set the pointer of 'encsubseg' into 'testsub'.  It has two purposes:
  //   (1) We can regonize it is encroached; (2) It is uniquely queued.
  setshell2badface(encsub->ss, encsub);
  // Add the subface to the end of a queue (quenumber = 2, high priority).
  *subquetail[quenumber] = encsub;
  // Maintain a pointer to the NULL pointer at the end of the queue.
  subquetail[quenumber] = &encsub->nextitem;
  if (b->verbose > 2) {
    printf("    Queuing subface (%d, %d, %d) [%d].\n", pointmark(encsub->forg),
           pointmark(encsub->fdest), pointmark(encsub->fapex), quenumber);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dequeueencsub()    Remove an enc-subface from the front of the queue.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::dequeueencsub(int* pquenumber)
{
  badface *result;
  int quenumber;

  // Look for a nonempty queue.
  for (quenumber = 2; quenumber >= 0; quenumber--) {
    result = subquefront[quenumber];
    if (result != (badface *) NULL) {
      // Remove the badface from the queue.
      subquefront[quenumber] = result->nextitem;
      // Maintain a pointer to the NULL pointer at the end of the queue.
      if (subquefront[quenumber] == (badface *) NULL) {
        subquetail[quenumber] = &subquefront[quenumber];
      }
      *pquenumber = quenumber;
      return result;
    }
  }
  return (badface *) NULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueuebadtet()    Add a tetrahedron into the queue.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueuebadtet(triface* testtet, REAL ratio2, REAL* cent)
{
  badface *newbadtet;
  int queuenumber;
  int i;

  // Allocate space for the bad tetrahedron.
  newbadtet = (badface *) badtetrahedrons->alloc();
  newbadtet->tt = *testtet;
  newbadtet->key = ratio2;
  if (cent != NULL) {
    for (i = 0; i < 3; i++) newbadtet->cent[i] = cent[i];
  } else {
    for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0;
  }
  newbadtet->forg = org(*testtet);
  newbadtet->fdest = dest(*testtet);
  newbadtet->fapex = apex(*testtet);
  newbadtet->foppo = oppo(*testtet);
  newbadtet->nextitem = (badface *) NULL;
  // Determine the appropriate queue to put the bad tetrahedron into.
  if (ratio2 > b->goodratio) {
    // queuenumber = (int) ((ratio2 - b->goodratio) / 0.5);
    queuenumber = (int) (64.0 - 64.0 / ratio2);
    // 'queuenumber' may overflow (negative) caused by a very large ratio.
    if ((queuenumber > 63) || (queuenumber < 0)) {
      queuenumber = 63;
    }
  } else {
    // It's not a bad ratio; put the tet in the lowest-priority queue.
    queuenumber = 0;
  }

  // Are we inserting into an empty queue?
  if (tetquefront[queuenumber] == (badface *) NULL) {
    // Yes. Will this become the highest-priority queue?
    if (queuenumber > firstnonemptyq) {
      // Yes, this is the highest-priority queue.
      nextnonemptyq[queuenumber] = firstnonemptyq;
      firstnonemptyq = queuenumber; 
    } else {
      // No. Find the queue with next higher priority.
      i = queuenumber + 1;
      while (tetquefront[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 tetrahedron at the beginning of the (empty) queue.
    tetquefront[queuenumber] = newbadtet;
  } else {
    // Add the bad tetrahedron to the end of an already nonempty queue.
    tetquetail[queuenumber]->nextitem = newbadtet;
  }
  // Maintain a pointer to the last tetrahedron of the queue.
  tetquetail[queuenumber] = newbadtet;

  if (b->verbose > 2) {
    printf("    Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n",
           pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
           pointmark(newbadtet->fapex), pointmark(newbadtet->foppo),
           sqrt(ratio2), queuenumber);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dequeuebadtet()    Remove a tetrahedron from the front of the queue.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::topbadtetra()
{
  // 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 tetquefront[firstnonemptyq];
  }
}

void tetgenmesh::dequeuebadtet()
{
  badface *deadbadtet;
  int i;

  // If queues were empty last time topbadtetra() was called, do nothing.
  if (recentq >= 0) {
    // Find the tetrahedron last returned by topbadtetra().
    deadbadtet = tetquefront[recentq];
    // Remove the tetrahedron from the queue.
    tetquefront[recentq] = deadbadtet->nextitem;
    // If this queue is now empty, update the list of nonempty queues.
    if (deadbadtet == tetquetail[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 (tetquefront[i] == (badface *) NULL) {
          i++;
        }
        nextnonemptyq[i] = nextnonemptyq[recentq];
      }
    }
    // Return the bad tetrahedron to the pool.
    badfacedealloc(badtetrahedrons, deadbadtet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkseg4encroach()    Check a subsegment to see if it is encroached.     //
//                                                                           //
// A segment s is encroached if there is a vertex lies inside or on its dia- //
// metral circumsphere, i.e., s faces an angle theta >= 90 degrees.          //
//                                                                           //
// If 'testpt' (p) != NULL, only test if 'testseg' (s) is encroached by it,  //
// else, check all apexes of faces around s. Return TRUE if s is encroached. //
// If and 'enqflag' is TRUE, add it into 'badsubsegs' if s is encroached.    //
//                                                                           //
// If 'prefpt' != NULL, it returns the reference point (defined in my paper) //
// if it exists.  This point is will be used to split s.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checkseg4encroach(face* testseg, point testpt, point* prefpt,
  bool enqflag)
{
  badface *encsubseg;
  triface starttet, spintet;
  point eorg, edest, eapex, encpt;
  REAL cent[3], radius, dist, diff;
  REAL maxradius;
  bool enq;
  int hitbdry;

  enq = false;
  eorg = sorg(*testseg);
  edest = sdest(*testseg);
  cent[0] = 0.5 * (eorg[0] + edest[0]);
  cent[1] = 0.5 * (eorg[1] + edest[1]);
  cent[2] = 0.5 * (eorg[2] + edest[2]);
  radius = distance(cent, eorg);

  if (varconstraint && (areabound(*testseg) > 0.0)) {
    enq = (2.0 * radius) > areabound(*testseg);
  }

  if (!enq) {
    maxradius = 0.0;
    if (testpt == (point) NULL) {
      // Check if it is encroached by traversing all faces containing it.
      sstpivot(testseg, &starttet);
      eapex = apex(starttet);
      spintet = starttet;
      hitbdry = 0;
      do {
        dist = distance(cent, apex(spintet));
        diff = dist - radius;
        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
        if (diff <= 0.0) {
          // s is encroached.
          enq = true;
          if (prefpt != (point *) NULL) {
            // Find the reference point.
            encpt = apex(spintet);
            circumsphere(eorg, edest, encpt, NULL, NULL, &dist);
            if (dist > maxradius) {
              // Rememebr this point.
              *prefpt = encpt;
              maxradius = dist;
            }
          } else {
            break;
          }
        }
        if (!fnextself(spintet)) {
          hitbdry++;
          if (hitbdry < 2) {
            esym(starttet, spintet);
            if (!fnextself(spintet)) {
              hitbdry++;
            } 
          }
        }
      } while (apex(spintet) != eapex && (hitbdry < 2));
    } else {
      // Only check if 'testseg' is encroached by 'testpt'.
      dist = distance(cent, testpt);
      diff = dist - radius;
      if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
      enq = (diff <= 0.0);
    }
  }

  if (enq && enqflag) {
    if (b->verbose > 2) {
      printf("    Queuing encroaching subsegment (%d, %d).\n",
             pointmark(eorg), pointmark(edest));
    }
    encsubseg = (badface *) badsubsegs->alloc();
    encsubseg->ss = *testseg;
    encsubseg->forg = eorg;
    encsubseg->fdest = edest;
    encsubseg->foppo = (point) NULL; // Not used.
    // Set the pointer of 'encsubseg' into 'testseg'.  It has two purposes:
    //   (1) We can regonize it is encroached; (2) It is uniquely queued.
    setshell2badface(encsubseg->ss, encsubseg);
  }
  
  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checksub4encroach()    Check a subface to see if it is encroached.        //
//                                                                           //
// A subface f is encroached if there is a vertex inside or on its diametral //
// circumsphere.                                                             //
//                                                                           //
// If 'testpt (p) != NULL', test if 'testsub' (f) is encroached by it, else, //
// test if f is encroached by one of the two opposites of the adjacent tets. //
// Return TRUE if f is encroached and queue it if 'enqflag' is set.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag)
{
  triface abuttet;
  point pa, pb, pc, encpt;
  REAL A[4][4], rhs[4], D;
  REAL cent[3], area;
  REAL radius, dist, diff;
  bool enq;
  int indx[4];
  int quenumber;
  
  enq = false;
  radius = 0.0;
  encpt = (point) NULL;

  pa = sorg(*testsub);
  pb = sdest(*testsub);
  pc = sapex(*testsub);
  
  // 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)

  if (varconstraint && (areabound(*testsub) > 0.0)) {
    // Check if the subface has too big area.
    area = 0.5 * sqrt(dot(A[2], A[2]));
    enq = area > areabound(*testsub);
    if (enq) {
      quenumber = 2; // A queue of subfaces having too big area.
    }
  }

  // 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]);
  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)) {
    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];
    radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
  }
  
  if (!enq) {
    // Check if the subface is encroached.
    if (testpt == (point) NULL) {
      stpivot(*testsub, abuttet);
      if (abuttet.tet != dummytet) {
        dist = distance(cent, oppo(abuttet));
        diff = dist - radius;
        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
        enq = (diff <= 0.0);
        if (enq) encpt = oppo(abuttet);
      }
      if (!enq) {
        sesymself(*testsub);
        stpivot(*testsub, abuttet);
        if (abuttet.tet != dummytet) {
          dist = distance(cent, oppo(abuttet));
          diff = dist - radius;
          if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
          enq = (diff <= 0.0);
          if (enq) encpt = oppo(abuttet);
        }
      }
    } else {
      dist = distance(cent, testpt);
      diff = dist - radius;
      if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
      enq = (diff <= 0.0);
    }
    if (enq) {
      quenumber = 0; // A queue of encroached subfaces.
    }
  }

  if (enq && enqflag) {
    enqueueencsub(testsub, encpt, quenumber, cent);    
  }

  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4badqual()    Test a tetrahedron for quality measures.            //
//                                                                           //
// Tests a tetrahedron to see if it satisfies the minimum ratio condition    //
// and the maximum volume condition. Tetrahedra that aren't upto spec are    //
// added to the bad tetrahedron queue.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4badqual(triface* testtet, bool enqflag)
{
  point pa, pb, pc, pd, pe1, pe2;
  REAL vda[3], vdb[3], vdc[3];
  REAL vab[3], vbc[3], vca[3]; 
  REAL N[4][3], A[4][4], rhs[4], D;
  REAL elen[6], circumcent[3];
  REAL bicent[3], offcent[3];
  REAL volume, L, cosd;
  REAL radius2, smlen2, ratio2;
  REAL dist, sdist, split;
  bool enq;
  int indx[4];
  int sidx, i, j; 

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

  // 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 rest 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];

  // Lu-decompose the matrix A.
  lu_decmp(A, 3, indx, &D, 0);
  // Get the volume of abcd.
  volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
  if (volume < 0.0) volume = -volume;
  // Check the radiu-edge ratio of the tet.
  rhs[0] = 0.5 * dot(vda, vda);
  rhs[1] = 0.5 * dot(vdb, vdb);
  rhs[2] = 0.5 * dot(vdc, vdc);
  lu_solve(A, 3, indx, rhs, 0);
  // Get the circumcenter.
  for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i];
  // Get the square of the circumradius.
  radius2 = dot(rhs, rhs);
  // Find the square of the shortest edge length.
  elen[0] = dot(vda, vda);
  elen[1] = dot(vdb, vdb);
  elen[2] = dot(vdc, vdc);
  elen[3] = dot(vab, vab);
  elen[4] = dot(vbc, vbc);
  elen[5] = dot(vca, vca);
  smlen2 = elen[0]; sidx = 0;
  for (i = 1; i < 6; i++) {
    if (smlen2 > elen[i]) { smlen2 = elen[i]; sidx = i; }
  }
  // Calculate the square of radius-edge ratio.
  ratio2 = radius2 / smlen2;
  // Check whether the ratio is smaller than permitted.
  enq = ratio2 > b->goodratio;
  if (!enq) {
    // abcd has good ratio.
    // ratio2 = 0.0;
    // if (b->offcenter) {
      // Test if it is a sliver.
      // Compute the 4 face normals (N[0], ..., N[3]).
      for (j = 0; j < 3; j++) {
        for (i = 0; i < 3; i++) rhs[i] = 0.0;
        rhs[j] = 1.0;  // Positive means the inside direction
        lu_solve(A, 3, indx, rhs, 0);
        for (i = 0; i < 3; i++) N[j][i] = rhs[i];
      }
      // Get the fourth normal by summing up the first three.
      for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
      // Normalized the normals.
      for (i = 0; i < 4; i++) {
        L = sqrt(dot(N[i], N[i]));
        if (L > 0.0) {
          for (j = 0; j < 3; j++) N[i][j] /= L;
        }
      }
      // N[0] is the normal of face bcd. Test the dihedral angles at edge
      //   cd, bd, and bc to see if they are too small or too big.
      for (i = 1; i < 4 && !enq; i++) {
        cosd = -dot(N[0], N[i]); // Edge cd, bd, bc.
        enq = cosd > cosmindihed;
      }
      if (!enq) {
        for (i = 2; i < 4 && !enq; i++) {
          cosd = -dot(N[1], N[i]); // Edge ad, ac
          enq = cosd > cosmindihed;
        }
        if (!enq) {
          cosd = -dot(N[2], N[3]); // Edge ab
          enq = cosd > cosmindihed;
        }
      }
    // }
  } else if (b->offcenter) {
    // abcd has bad-quality. Use off-center instead of circumcenter.
    switch (sidx) {
    case 0: // edge da.
      pe1 = pd; pe2 = pa; break;
    case 1: // edge db.
      pe1 = pd; pe2 = pb; break;
    case 2: // edge dc.
      pe1 = pd; pe2 = pc; break;
    case 3: // edge ab.
      pe1 = pa; pe2 = pb; break;
    case 4: // edge bc.
      pe1 = pb; pe2 = pc; break;
    case 5: // edge ca.
      pe1 = pc; pe2 = pa; break;
    default: 
      pe1 = pe2 = (point) NULL; // Avoid a compile warning.
    }
    // The shortest edge is e1->e2.
    for (i = 0; i < 3; i++) bicent[i] = 0.5 * (pe1[i] + pe2[i]);
    dist = distance(bicent, circumcent);
    // sdist = sqrt(smlen2) * sin(PI / 3.0); // A icoso-triangle.
    // The following formulae is from 
    sdist = b->alpha3 * (b->minratio+sqrt(b->goodratio-0.25))* sqrt(smlen2);
    split = sdist / dist;
    if (split > 1.0) split = 1.0;
    // Get the off-center.
    for (i = 0; i < 3; i++) {
      offcent[i] = bicent[i] + split * (circumcent[i] - bicent[i]);
    }
  }

  if (!enq && (b->varvolume || b->fixedvolume)) {
    // Check if the tet has too big volume.
    enq = b->fixedvolume && (volume > b->maxvolume);
    if (!enq && b->varvolume) {
      enq = (volume > volumebound(testtet->tet)) &&
            (volumebound(testtet->tet) > 0.0);
    }
  }

  if (!enq) {
    // Check if the user-defined sizing function is satisfied. 
    if (b->metric) {
      // assert(b->alpha1 > 0.0);
      sdist = sqrt(radius2) / b->alpha1;
      for (i = 0; i < 4; i++) {
        pa = (point) testtet->tet[4 + i];
        // Get the indicated size of p.
        dist = pa[pointmtrindex]; // dist = b->alpha1 * pa[pointmtrindex];
        enq = ((dist < sdist) && (dist > 0.0));
        if (enq) break; // It is bad wrt. a node constraint.
        // *** Experiment ! Stop test if c is inside H(a).
        // if ((dist > 0.0) && (dist > sdist)) break;
      }
      // *** Experiment !
      // enq = (i == 4); // Does c lies outside all sparse-ball?
    } // if (b->metric)
  }

  if (enq && enqflag) {
    if (b->offcenter && (ratio2 > b->goodratio)) {
      for (i = 0; i < 3; i++) circumcent[i] = offcent[i];
    }
    enqueuebadtet(testtet, ratio2, circumcent);
  }

  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptsegpt()    Check if a segment point can be inserted or not.         //
//                                                                           //
// Segment(ab) is indicated to be split by a point p (\in ab). This routine  //
// decides whether p can be inserted or not.                                 //
//                                                                           //
// p can not be inserted either the '-Y' option is used and ab is a hull     //
// segment or '-YY' option is used.                                          //
//                                                                           //
// p can be inserted if it is in one of the following cases:                 //
//   (1) if L = |a - b| is too long wrt the edge constraint; or              //
//   (2) if |x - p| > \alpha_2 H(x) for x = a, b; or                         //
//   (3) if 'refpt' != NULL.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptsegpt(point segpt, point refpt, face* splitseg)
{
  point p[2];
  REAL L, lfs;
  int i, j;

  if (b->nobisect == 1) {
    // '-Y'. It can not be split if it is on the hull.
    triface spintet;
    point pc;
    sstpivot(splitseg, &spintet);
    assert(spintet.tet != dummytet);
    pc = apex(spintet);
    do {
      if (!fnextself(spintet)) {
        // Meet a boundary face - s is on the hull.
        return false;
      }
    } while (pc != apex(spintet));
  } else if (b->nobisect > 1) {
    // '-YY'. Do not split it.
    return false;
  }
  
  p[0] = sorg(*splitseg);
  p[1] = sdest(*splitseg);
  if (varconstraint && (areabound(*splitseg) > 0)) {
    lfs = areabound(*splitseg);
    L = distance(p[0], p[1]);
    if (L > lfs) {
      return true; // case (1)
    }
  }

  j = 0; // Use j to count the number of inside balls.
  for (i = 0; i < 2; i++) {
    // Check if p is inside the protect ball of q.
    if (p[i][pointmtrindex] > 0.0) {
      lfs = b->alpha2 * p[i][pointmtrindex];
      L = distance(p[i], segpt);
      if (L < lfs) j++; // p is inside ball.
    }
  }
  if (j == 0) return true; // case (3).

  // If 'refpt' != NULL, force p to be inserted.
  if (refpt != (point) NULL) {
    cdtenforcesegpts++;
    return true;
  }

  // Do not split it.
  rejsegpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptfacpt()    Check if a facet point can be inserted or not.           //
//                                                                           //
// 'subceillist' is CBC(p). 'verlist' (V) is empty on input, it returns the  //
// set of vertices of CBC(p).                                                //
//                                                                           //
// p can not be inserted either the '-Y' option is used and the facet is on  //
// the hull or '-YY' option is used.                                         //
//                                                                           //
// p can be inserted if |p - v| > \alpha_2 H(v), for all v \in V.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptfacpt(point facpt, list* subceillist, list* verlist)
{
  face *testsh;
  point p[2], ploop;
  REAL L, lfs;
  int idx, i, j;

  if (b->nobisect == 1) {
    // '-Y'. p can not be inserted if CBC(p) is on the hull.
    triface testtet;
    testsh = (face *)(* subceillist)[0];
    stpivot(*testsh, testtet);
    if (testtet.tet != dummytet) {
      sesymself(*testsh);
      stpivot(*testsh, testtet);
    }
    if (testtet.tet == dummytet) return false;
  } else if (b->nobisect > 1) {
    // '-YY'. Do not split s.
    return false;
  }

  // Collect the vertices of CBC(p), save them in V.
  for (i = 0; i < subceillist->len(); i++) {
    testsh = (face *)(* subceillist)[i];
    p[0] = sorg(*testsh);
    p[1] = sdest(*testsh);
    for (j = 0; j < 2; j++) {
      idx = pointmark(p[j]);
      if (idx >= 0) {
        setpointmark(p[j], -idx - 1);
        verlist->append(&(p[j]));
      }
    }
  }

  j = 0; // Use j to count the number of inside balls.
  for (i = 0; i < verlist->len(); i++) {
    ploop = * (point *)(* verlist)[i];
    // Uninfect q.
    idx = pointmark(ploop);
    setpointmark(ploop, -(idx + 1)); 
    // Check if p is inside the protect ball of q.
    if (ploop[pointmtrindex] > 0.0) {
      lfs = b->alpha2 * ploop[pointmtrindex];
      L = distance(ploop, facpt);
      if (L < lfs) j++; // p is inside ball.
    }
  }
  verlist->clear();

  if (j == 0) return true; // case (3).

  rejsubpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptvolpt()    Check if a volume point can be inserted or not.          //
//                                                                           //
// 'ceillist' is B(p).  'verlist' (V) is empty on input, it returns the set  //
// of vertices of B(p).                                                      //
//                                                                           //
// p can be split if |p - v| > \alpha_2 H(v), for all v \in V.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptvolpt(point volpt, list* ceillist, list* verlist)
{
  triface* testtet;
  point p[3], ploop;
  REAL L, lfs;
  int idx, i, j;

  // Collect the vertices of CBC(p), save them in V.
  for (i = 0; i < ceillist->len(); i++) {
    testtet = (triface *)(* ceillist)[i];
    p[0] = org(*testtet);
    p[1] = dest(*testtet);
    p[2] = apex(*testtet);
    for (j = 0; j < 3; j++) {
      idx = pointmark(p[j]);
      if (idx >= 0) {
        setpointmark(p[j], -idx - 1);
        verlist->append(&(p[j]));
      }
    }
  }

  j = 0; // Use j to counte the number of inside balls.
  for (i = 0; i < verlist->len(); i++) {
    ploop = * (point *)(* verlist)[i];
    // Uninfect q.
    idx = pointmark(ploop);
    setpointmark(ploop, -(idx + 1));
    // Check if p is inside the protect ball of q.
    if (ploop[pointmtrindex] > 0.0) {
      lfs = b->alpha2 * ploop[pointmtrindex];
      L = distance(ploop, volpt);
      if (L < lfs) j++; // p is inside the protect ball.
    }
  }
  verlist->clear();
  
  if (j == 0) return true; // case (2).

  rejtetpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsplitpoint()    Get the inserting point in a segment.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt)
{
  point ei, ej;
  REAL split, L, d1, d2, ps, rs;
  bool acutea, acuteb;
  int i;

  if (refpt != (point) NULL) {
    // Use the CDT rules to split the segment.
    acutea = (pointtype(e1) == ACUTEVERTEX);
    acuteb = (pointtype(e2) == ACUTEVERTEX);
    if (acutea ^ acuteb) {
      // Only one endpoint is acute. Use rule-2 or rule-3.
      ei = acutea ? e1 : e2;
      ej = acutea ? e2 : e1;
      L = distance(ei, ej);
      // Apply rule-2.
      d1 = distance(ei, refpt);
      split = d1 / L;
      for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]);
      // Check if rule-3 is needed.
      d2 = distance(refpt, newpt);
      if (d2 > (L - d1)) {
        // Apply rule-3.
        if ((d1 - d2) > (0.5 * d1)) {
          split = (d1 - d2) / L;
        } else {
          split = 0.5 * d1 / L;
        }
        for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]);
        if (b->verbose > 1) {
          printf("    Found by rule-3:");
        }
        r3count++;
      } else {
        if (b->verbose > 1) {
          printf("    Found by rule-2:");
        }
        r2count++;
      }
      if (b->verbose > 1) {
        printf(" center %d, split = %.12g.\n", pointmark(ei), split);
      }
      // Add a random perturbation on newpt.
      d1 = distance(ei, newpt);
      d2 = distance(newpt, refpt);
      ps = randgenerator(d2 * b->epsilon2 * 1e+2);
      rs = ps / d1;
      // Perturb newpt away from ei.
      for (i = 0; i < 3; i++) newpt[i] = ei[i] + (1.0+rs) * (newpt[i] - ei[i]);
    } else {
      // Both endpoints are acute or not. Split it at the middle.
      for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]);
      // Add a random perturbation on newpt.
      d1 = 0.5 * distance(e1, e2);
      ps = randgenerator(d1 * b->epsilon2 * 1e+2);
      rs = ps / d1;
      for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]);
    }
  } else {
    // Split the segment at its midpoint.
    for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]);
    // Add a random perturbation on newpt.
    d1 = 0.5 * distance(e1, e2);
    ps = randgenerator(d1 * b->epsilon2 * 1e+2);
    rs = ps / d1;
    for (i = 0; i < 3; i++) newpt[i] = e1[i] + (1.0+rs) * (newpt[i] - e1[i]);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shepardinterpolation()    Interpolate the local size of a newpoint.       //
//                                                                           //
// The classical Shepard interoplation (inversed weighted distance) is used. //
// (With the choice p = 2).                                                  //
//                                                                           //
// 'verlist' contains a list vertices neighboring to 'newpt'.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::shepardinterpolate(point newpt, list *verlist)
{
  point neipt;
  REAL *weights, sumweight;
  REAL vec[3];
  int i, j;

  weights = new REAL[verlist->len()];
  sumweight = 0.0;

  // Calculate the weight of each point.
  for (i = 0; i < verlist->len(); i++) {
    neipt = * (point *)(* verlist)[i];
    for (j = 0; j < 3; j++) vec[j] = neipt[j] - newpt[j];
    weights[i] = 1.0 / dot(vec, vec);
    sumweight += weights[i];
  }
  // Interpolate.
  newpt[pointmtrindex] = 0.0;
  for (i = 0; i < verlist->len(); i++) {
    neipt = * (point *)(* verlist)[i];
    newpt[pointmtrindex] += (weights[i] * neipt[pointmtrindex]) / sumweight;
  }

  delete [] weights;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// setnewpointsize()    Set the size for a new point.                        //
//                                                                           //
// The size of the new point p is interpolated either from a background mesh //
// (b->bgmesh) or from the two input endpoints.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::setnewpointsize(point newpt, point e1, point e2)
{
  if (b->metric) {
    // Interpolate the point size in a background mesh.
    triface bgmtet;
    // Get a tet in background mesh for locating p.
    decode(point2bgmtet(e1), bgmtet);
    p1interpolatebgm(newpt, &bgmtet, NULL);
  } else {
    if (e2 != (point) NULL) {
      // Interpolate the size between the two endpoints.
      REAL split, l, d;
      l = distance(e1, e2);
      d = distance(e1, newpt);
      split = d / l;
#ifdef SELF_CHECK
      // Check if e1 and e2 are endpoints of a sharp segment.
      assert(e1[pointmtrindex] > 0.0);
      assert(e2[pointmtrindex] > 0.0);
#endif
      newpt[pointmtrindex] = (1.0 - split) * e1[pointmtrindex] 
                           + split * e2[pointmtrindex];
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitencseg()    Split an enc-seg and recover the Delaunayness by flips.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist,
  list* sublist, list* verlist, queue* flipque, bool chkencsub, bool chkbadtet,
  bool optflag)
{
  list *mytetlist;
  queue *myflipque;
  triface starttet;
  face startsh, spinsh, checksh;
  int i;

  if (optflag) {
    mytetlist = new list(sizeof(triface), NULL, 1024);
    myflipque = new queue(sizeof(badface));
    tetlist = mytetlist;
    flipque = myflipque;
  }

  // Use the base orientation (important in this routine).
  splitseg->shver = 0;
  // Insert p, this should always success.
  sstpivot(splitseg, &starttet);
  splittetedge(newpt, &starttet, flipque);
  // Remove locally non-Delaunay faces by flipping.
  flip(flipque, NULL); // lawson(NULL, flipque);
  
  if (!optflag) { 
    // Check the two new subsegs to see if they're encroached (not by p).
    for (i = 0; i < 2; i++) {
      if (!shell2badface(*splitseg)) {
        checkseg4encroach(splitseg, NULL, NULL, true);
      }
      if (i == 1) break; // Two new segs have been checked.
      senextself(*splitseg);
      spivotself(*splitseg);
#ifdef SELF_CHECK
      assert(splitseg->sh != (shellface *) NULL);
#endif
      splitseg->shver = 0;
    }
    // Check the new subfaces to see if they're encroached (not by p).
    if (chkencsub) {
      spivot(*splitseg, startsh);
      spinsh = startsh;
      do {
        sublist->append(&spinsh);
        formstarpolygon(newpt, sublist, verlist);
        for (i = 0; i < sublist->len(); i++) {
          checksh = * (face *)(* sublist)[i];
          if (!shell2badface(checksh)) {
            checksub4encroach(&checksh, NULL, true);
          }
        }
        sublist->clear();
        if (verlist) verlist->clear();
        spivotself(spinsh);
      } while (spinsh.sh != startsh.sh);
    }
  } // if (!optflag)  

  // Collect the new tets connecting at p.
  sstpivot(splitseg, &starttet);
  tetlist->append(&starttet);
  formstarpolyhedron(newpt, tetlist, verlist, true);

  if (!optflag) {
    // Check if p encroaches adjacent segments.
    tallencsegs(newpt, 1, &tetlist);
    if (chkencsub) {
      // Check if p encroaches adjacent subfaces.
      tallencsubs(newpt, 1, &tetlist);
    }
    if (chkbadtet) {
      // Check if there are new bad quality tets at p.
      for (i = 0; i < tetlist->len(); i++) {
        starttet = * (triface *)(* tetlist)[i];
        checktet4badqual(&starttet, true);
      }
    }
    tetlist->clear();
  } else {
    // Check if new tets are non-optimal.
    for (i = 0; i < tetlist->len(); i++) {
      starttet = * (triface *)(* tetlist)[i];
      checktet4opt(&starttet, true);
    }
    delete mytetlist;
    delete myflipque;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallencsegs()    Check for encroached segments and save them in list.     //
//                                                                           //
// If 'testpt' (p) != NULL, only check if segments are encroached by p, else,//
// check all the nearby mesh vertices.                                       //
//                                                                           //
// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the    //
// segments which are on B_i(p)s, else, check the entire list of segments    //
// (in the pool 'this->subsegs').                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists)
{
  list *ceillist;
  triface ceiltet;
  face checkseg;
  long oldencnum;
  int i, j, k;
  
  // Remember the current number of encroached segments.
  oldencnum = badsubsegs->items;

  if (ceillists != (list **) NULL) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      // Check the segments on B_i(p).
      for (i = 0; i < ceillist->len(); i++) {
        ceiltet = * (triface *)(* ceillist)[i];
        ceiltet.ver = 0;
        for (j = 0; j < 3; j++) {
          tsspivot(&ceiltet, &checkseg);
          if (checkseg.sh != dummysh) {
            // Found a segment. Test it if it isn't in enc-list.
            if (!shell2badface(checkseg)) {
              checkseg4encroach(&checkseg, testpt, NULL, true);
            }
          }
          enextself(ceiltet);
        }
      }
    }
  } else {
    // Check the entire list of segments.
    subsegs->traversalinit();
    checkseg.sh = shellfacetraverse(subsegs);
    while (checkseg.sh != (shellface *) NULL) {
      // Test it if it isn't in enc-list.
      if (!shell2badface(checkseg)) {
        checkseg4encroach(&checkseg, testpt, NULL, true);
      }
      checkseg.sh = shellfacetraverse(subsegs);
    }
  }

  return (badsubsegs->items > oldencnum);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallencsubs()    Find all encroached subfaces and save them in list.      //
//                                                                           //
// If 'testpt' (p) != NULL, only check if subfaces are encroached by p, else,//
// check the adjacent vertices of subfaces.                                  //
//                                                                           //
// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the    //
// subfaces which are on B_i(p)s, else, check the entire list of subfaces    //
// (in the pool 'this->subfaces').                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists)
{
  list *ceillist;
  triface ceiltet;
  face checksh;
  long oldencnum;
  int i, k;
  
  // Remember the current number of encroached segments.
  oldencnum = badsubfaces->items;

  if (ceillists != (list **) NULL) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      // Check the subfaces on B_i(p).
      for (i = 0; i < ceillist->len(); i++) {
        ceiltet = * (triface *)(* ceillist)[i];
        tspivot(ceiltet, checksh);
        if (checksh.sh != dummysh) {
          // Found a subface. Test it if it isn't in enc-list.
          if (!shell2badface(checksh)) {
            checksub4encroach(&checksh, testpt, true);
          }
        }
      }
    }
  } else {
    // Check the entire list of subfaces.
    subfaces->traversalinit();
    checksh.sh = shellfacetraverse(subfaces);
    while (checksh.sh != (shellface *) NULL) {
      // Test it if it isn't in enc-list.
      if (!shell2badface(checksh)) {
        checksub4encroach(&checksh, testpt, true);
      }
      checksh.sh = shellfacetraverse(subfaces);
    }
  }

  return (badsubfaces->items > oldencnum);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallbadtetrahedrons()    Queue all the bad-quality tetrahedra in the mesh.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallbadtetrahedrons()
{
  triface tetloop;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    checktet4badqual(&tetloop, true);
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencsegs()    Repair (split) all the encroached segments.            //
//                                                                           //
// Each encroached segment is repaired by splitting it - inserting a vertex  //
// at or near its midpoint.  Newly inserted vertices may encroach upon other //
// subsegments, these are also repaired.                                     //
//                                                                           //
// 'chkencsub' and 'chkbadtet' are two flags that specify whether one should //
// take note of new encroaced subfaces and bad quality tets that result from //
// inserting vertices to repair encroached subsegments.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet)
{
  list **tetlists, **ceillists;
  list **sublists, **subceillists;
  list *tetlist, *sublist;
  queue *flipque;
  badface *encloop;
  face splitseg, symsplitseg;
  point newpt, sympt, refpt;
  point e1, e2;
  enum locateresult symloc;
  int nmax, n, i, j;

  n = 0;
  nmax = 128;
  if (!b->fliprepair) {
    tetlists = new list*[nmax];
    ceillists = new list*[nmax];
    sublists = new list*[nmax];
    subceillists = new list*[nmax];
  } else {
    tetlist = new list(sizeof(triface), NULL, 1024);
    sublist = new list(sizeof(face), NULL, 256);
    flipque = new queue(sizeof(badface));
  }

  // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubsegs->items > 0) && (steinerleft != 0)) {
    badsubsegs->traversalinit();
    encloop = badfacetraverse(badsubsegs);
    while ((encloop != (badface *) NULL) && (steinerleft != 0)) {
      // Get an encroached subsegment s.
      splitseg = encloop->ss;
      // Clear the in-queue flag in s.
      setshell2badface(splitseg, NULL);
      if ((sorg(splitseg) == encloop->forg) && 
          (sdest(splitseg) == encloop->fdest)) {
        if (b->verbose > 1) {
          printf("  Get an enc-seg (%d, %d)\n", pointmark(encloop->forg),
                 pointmark(encloop->fdest));
        }
        refpt = (point) NULL;
        if (b->conformdel) {
          // Look for a reference point.
          checkseg4encroach(&splitseg, NULL, &refpt, false);
        }
        // Create the new point p (at the middle of s).
        makepoint(&newpt);
        getsplitpoint(encloop->forg, encloop->fdest, refpt, newpt);
        setpointtype(newpt, FREESEGVERTEX);
        setpoint2sh(newpt, sencode(splitseg));
        // Decide whether p can be inserted or not.
        if (acceptsegpt(newpt, refpt, &splitseg)) {
          // Is there periodic boundary condition?
          if (checkpbcs) {
            // Insert points on other segments of incident pbcgroups.
            i = shellmark(splitseg) - 1;
            for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++) {
              makepoint(&sympt);
              symloc = getsegpbcsympoint(newpt, &splitseg, sympt, &symsplitseg,
                                         segpglist[j]);
              if (symloc == ONEDGE) {
                if (symsplitseg.sh != splitseg.sh) {
                  // Insert sympt.
                  setpointtype(sympt, FREESEGVERTEX);
                  setpoint2sh(sympt, sencode(symsplitseg));
                  // Save the endpoints of the seg for size interpolation.
                  e1 = sorg(symsplitseg);
                  if (shelltype(symsplitseg) == SHARP) {
                    e2 = sdest(symsplitseg);
                  } else {
                    e2 = (point) NULL; // No need to do size interpolation.
                  }
                  if (!b->fliprepair) {
                    // Form BC(symp), B(symp), CBC(symp)s, C(symp)s.
                    formbowatcavity(sympt, &symsplitseg, NULL, &n, &nmax,
                      sublists, subceillists, tetlists, ceillists);
                    // Validate BC(symp), B(symp), CBC(symp)s, C(symp)s.
                    if (trimbowatcavity(sympt, &symsplitseg, n, sublists,
                          subceillists, tetlists, ceillists, -1.0)) {
                      bowatinsertsite(sympt, &symsplitseg, n, sublists,
                        subceillists, tetlists, ceillists, NULL, flipque,
                        true, chkencsub, chkbadtet);
                      setnewpointsize(sympt, e1, e2);
                      if (steinerleft > 0) steinerleft--;
                    } else {
                      // p did not insert for invalid BC(symp).
                      pointdealloc(sympt);
                    }
                    // Free the memory allocated in formbowatcavity().
                    releasebowatcavity(&symsplitseg, n, sublists, subceillists,
                                       tetlists, ceillists);
                  } else {
                    splitencseg(sympt, &symsplitseg, tetlist, sublist, NULL,
                                flipque, chkencsub, chkbadtet, false);
                    setnewpointsize(sympt, e1, e2);
                    if (steinerleft > 0) steinerleft--;
                  }
                } else {
                  // The sympt are on the same segment. It is possible when
                  //   splitseg is the symmetric rotating axes.
                  pointdealloc(sympt);
                }
              } else if (symloc == ONVERTEX) {
                // The sympt already exists. It is possible when two pbc
                //   groups are exactly the same. Omit this point.
                pointdealloc(sympt);
              } else {
                // Do not isnert symp for unknown cases: ONFACE, OUTSIDE.
                // assert(0);
                pointdealloc(sympt);
              }
            } // for (j = idx2segpglist[i]; j < idx2segpglist[i + 1]; j++)
          } // if (checkpbcs)
          // Save the endpoints of the seg for size interpolation.
          e1 = sorg(splitseg);
          if (shelltype(splitseg) == SHARP) {
            e2 = sdest(splitseg);
          } else {
            e2 = (point) NULL; // No need to do size interoplation.
          }
          if (!b->fliprepair) {
            // Form BC(p), B(p), CBC(p)s, and C(p)s.
            formbowatcavity(newpt, &splitseg, NULL, &n, &nmax, sublists,
                            subceillists, tetlists, ceillists);
            // Validate/update BC(p), B(p), CBC(p)s, and C(p)s.
            if (trimbowatcavity(newpt, &splitseg, n, sublists, subceillists, 
                                tetlists, ceillists, -1.0)) {
              bowatinsertsite(newpt, &splitseg, n, sublists, subceillists,
                              tetlists, ceillists, NULL, flipque, true,
                              chkencsub, chkbadtet);
              setnewpointsize(newpt, e1, e2);
              if (steinerleft > 0) steinerleft--;
            } else {
              // p did not insert for invalid B(p).
              pointdealloc(newpt);
            }
            // Free the memory allocated in formbowatcavity().
            releasebowatcavity(&splitseg, n, sublists, subceillists, tetlists,
                               ceillists);
          } else {
            splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque,
                        chkencsub, chkbadtet, false);
            setnewpointsize(newpt, e1, e2);
            if (steinerleft > 0) steinerleft--;
          }
        } else {
          // p did not accept for insertion.
          pointdealloc(newpt);
        } // if (checkseg4splitting(newpt, &splitseg))
      } // if ((encloop->forg == pa) && (encloop->fdest == pb))
      badfacedealloc(badsubsegs, encloop); // Remove this entry from list.
      encloop = badfacetraverse(badsubsegs); // Get the next enc-segment.
    } // while ((encloop != (badface *) NULL) && (steinerleft != 0))
  } // while ((badsubsegs->items > 0) && (steinerleft != 0))

  if (!b->fliprepair) {
    delete [] tetlists;
    delete [] ceillists;
    delete [] sublists;
    delete [] subceillists;
  } else {
    delete tetlist;
    delete sublist;
    delete flipque;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencsubs()    Repair (split) all the encroached subfaces.            //
//                                                                           //
// Each encroached subface is repaired by splitting it - inserting a vertex  //
// at or near its circumcenter.  Newly inserted vertices may encroach upon   //
// other subfaces, these are also repaired.                                  //
//                                                                           //
// 'chkbadtet' is a flag that specifies whether one should take note of new  //
// bad quality tets that result from inserted vertices.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencsubs(bool chkbadtet)
{
  list *tetlists[2], *ceillists[2];
  list *sublist, *subceillist;
  list *verlist;
  badface *encloop;
  face splitsub, symsplitsub;
  point newpt, sympt, e1;
  enum locateresult loc, symloc;
  bool reject;
  long oldptnum;
  int quenumber, n, i;

  n = 0;
  sublist = (list *) NULL;
  subceillist = (list *) NULL;
  verlist = new list(sizeof(point *), NULL, 256);

  // Loop until the pool 'badsubfaces' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubfaces->items > 0) && (steinerleft != 0)) {
    // Get an encroached subface f.
    encloop = dequeueencsub(&quenumber);
    splitsub = encloop->ss;
    // Clear the in-queue flag of f.
    setshell2badface(splitsub, NULL);
    // f may not be the same one when it was determined to be encroached.
    if (!isdead(&splitsub)
        && (sorg(splitsub) == encloop->forg)
        && (sdest(splitsub) == encloop->fdest)
        && (sapex(splitsub) == encloop->fapex)) {
      if (b->verbose > 1) {
        printf("    Dequeuing ensub (%d, %d, %d) [%d].\n",
               pointmark(encloop->forg), pointmark(encloop->fdest),
               pointmark(encloop->fapex), quenumber);
      }
      // Create a new point p at the circumcenter of f.
      makepoint(&newpt);
      for (i = 0; i < 3; i++) newpt[i] = encloop->cent[i];
      setpointtype(newpt, FREESUBVERTEX);
      setpoint2sh(newpt, sencode(splitsub));
      // Set the abovepoint of f for point location.
      abovepoint = facetabovepointarray[shellmark(splitsub)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&splitsub);
      }
      // Locate p, start from f, stop at segment (1), use a tolerance to
      //   detect ONVERTEX or OUTSIDE case. Update f on return.
      loc = locatesub(newpt, &splitsub, 1, b->epsilon * 1e+2);
      if ((loc != ONVERTEX) && (loc != OUTSIDE)) {
        // Form BC(p), B(p), CBC(p) and C(p).
        formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist,
                        &subceillist, tetlists, ceillists);
        // Check for encroached subsegments (on B(p)).
        reject = tallencsegs(newpt, 2, ceillists);
        // Execute point accept rule if p does not encroach upon any segment.
        if (!reject) {
          reject = !acceptfacpt(newpt, subceillist, verlist);
        }
        if (!reject) {
          // Validate/update cavity.
          reject = !trimbowatcavity(newpt, NULL, n, &sublist, &subceillist,
                                    tetlists, ceillists, -1.0);
        }
        if (!reject) {
          // CBC(p) should include s, so that s can be removed after CBC(p)
          //   is remeshed. However, if there are locally non-Delaunay faces
          //   and encroached subsegments, s may not be collected in CBC(p).
          //   p should not be inserted in such case.
          reject = !sinfected(encloop->ss);
        }
        if (!reject) {
          if (checkpbcs) {
            if (shellpbcgroup(splitsub) >= 0) {
              // Check for splitting of the symmetric subface of f.
              makepoint(&sympt);
              symloc = getsubpbcsympoint(newpt,&splitsub,sympt,&symsplitsub);
              if (symloc != ONVERTEX) {
                // Release CBC(p) and BC(p) and free the memory..
                releasebowatcavity(NULL, 2, &sublist, &subceillist, tetlists,
                                   ceillists);
                // Form CBC(symp), C(symp), BC(sympt) and B(sympt).
                formbowatcavity(sympt, NULL, &symsplitsub, &n, NULL, &sublist,
                                &subceillist, tetlists, ceillists);
                reject = tallencsegs(sympt, 2, ceillists);
                if (!reject) {
                  reject = !acceptfacpt(sympt, subceillist, verlist);
                }
                if (!reject) {
                  reject = !trimbowatcavity(sympt,NULL,n,&sublist,&subceillist,
                                            tetlists, ceillists, -1.0);
                }
                if (!reject) {
                  // Insert sympt.
                  setpoint2pbcpt(newpt, sympt);
                  setpoint2pbcpt(sympt, newpt);
                  setpointtype(sympt, FREESUBVERTEX);
                  setpoint2sh(sympt, sencode(symsplitsub));
                  // Save a point for size interpolation.
                  e1 = sorg(symsplitsub);
                  bowatinsertsite(sympt, NULL, n, &sublist, &subceillist,
                     tetlists,ceillists,NULL,NULL,false,true,chkbadtet);
                  setnewpointsize(sympt, e1, NULL);
                  if (steinerleft > 0) steinerleft--;
                  // Release CBC(symp) and BC(symp) and free the memory..
                  releasebowatcavity(NULL, n, &sublist, &subceillist, tetlists,
                                     ceillists);
                } else {
                  // symp is rejected for one of the following reasons:
                  //   (1) BC(symp) is not valid; or
                  //   (2) symp encroaches upon some subsegments (queued); or
                  //   (3) symp is rejected by point accepting rule.
                  pointdealloc(sympt);
                  // Cavity will be released by the following code.
                }
              } else {
                // Do not insert sympt for invalid PBC data.
                pointdealloc(sympt);
                // p is rejected due to symp.
                reject = true;
              }
            }
          } // if (checkpbcs)
        }
        if (!reject) {
          // Insert p.
          if (checkpbcs) {
            if (shellpbcgroup(splitsub) >= 0) {
              // Form CBC(p), C(p), BC(p) and B(p).
              formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist,
                              &subceillist, tetlists, ceillists);
              trimbowatcavity(newpt, NULL, n, &sublist, &subceillist, tetlists,
                              ceillists, -1.0);
            }
          }
          // Save a point for size interpolation.
          e1 = sorg(splitsub);
          bowatinsertsite(newpt, NULL, n, &sublist, &subceillist, tetlists,
                          ceillists, NULL, NULL, true, true, chkbadtet);
          setnewpointsize(newpt, e1, NULL);
          if (steinerleft > 0) steinerleft--;
        } else {
          // p is rejected for the one of the following reasons:
          //   (1) BC(p) is not valid.
          //   (2) s does not in CBC(p).
          //   (3) p encroaches upon some segments (queued); or
          //   (4) p is rejected by point accepting rule, or
          //   (5) due to the rejection of symp (the PBC).
          pointdealloc(newpt);
        } // if (!reject)
        // Release the cavity and free the memory.
        releasebowatcavity(NULL,n,&sublist,&subceillist,tetlists,ceillists);
        if (reject) {
          // Are there queued encroached subsegments.
          if (badsubsegs->items > 0) {
            // Repair enc-subsegments.
            oldptnum = points->items;
            repairencsegs(true, chkbadtet);
            if (points->items > oldptnum) {
              // Some enc-subsegments got split. Try to repair f later.
              splitsub = encloop->ss;
              if (!isdead(&splitsub)) {
                if (!shell2badface(splitsub)) {
                  checksub4encroach(&splitsub, NULL, true);
                }
              }
            }
          }
        }
      } else {
        // Don't insert p for one of the following reasons:
        //   (1) Locate on an existing vertex; or
        //   (2) locate outside the domain.
        // Case (1) should not be possible. If such vertex v exists, it is
        //   the circumcenter of f, ie., f is non-Delaunay. Either f was got
        //   split before by v, but survived after v was inserted, or the
        //   same for a f' which is nearly co-circular with f.  Whatsoever,
        //   there are encroached segs by v, but the routine tallencsegs()
        //   did not find them out.
        if (loc == ONVERTEX) {
          printf("Internal error in repairencsubs():\n");
          printf("  During repairing encroached subface (%d, %d, %d)\n",
                 pointmark(encloop->forg), pointmark(encloop->fdest),
                 pointmark(encloop->fapex));
          printf("  New point %d is coincident with an existing vertex %d\n",
                 pointmark(newpt), pointmark(sorg(splitsub)));
          internalerror();
        }
        // Case (2) can happen when thers is a segment s which is close to f
        //   and is non-conforming Delaunay. The circumcenter of f encroaches
        //   upon s, but the circumcenter of s is rejected for insertion.
        pointdealloc(newpt);
      } // if ((loc != ONVERTEX) && (loc != OUTSIDE))
    } else {
      if (!isdead(&splitsub)) {
        // The subface has been changed, re-check it.
        checksub4encroach(&splitsub, NULL, true);
      }
    } // if (!isdead(&splitsub) && (sorg(splitsub) == encloop->forg) &&
    // Remove this entry from list.
    badfacedealloc(badsubfaces, encloop);
  } // while ((badsubfaces->items > 0) && (steinerleft != 0))

  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairbadtets()    Repair all bad-quality tetrahedra.                     //
//                                                                           //
// All bad-quality tets are stored in pool 'badtetrahedrons'.  Each bad tet  //
// is repaired by inserting a point at or near its circumcenter. However, if //
// this point encroaches any subsegment or subface, it is not inserted. Ins- //
// tead the encroached segment and subface are split.  Newly inserted points //
// may create other bad-quality tets, these are also repaired.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairbadtets()
{
  list *tetlist, *ceillist;
  list *verlist;
  badface *badtet;
  triface starttet;
  point newpt, e1;
  enum locateresult loc;
  bool reject;
  long oldptnum;
  int i;

  tetlist = new list(sizeof(triface), NULL, 1024);
  ceillist = new list(sizeof(triface), NULL, 1024);
  verlist = new list(sizeof(point *), NULL, 256);

  // Loop until pool 'badtetrahedrons' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badtetrahedrons->items > 0) && (steinerleft != 0)) {
    // Get a bad-quality tet t.
    badtet = topbadtetra();
    // Make sure that the tet is still the same one when it was tested.
    //   Subsequent transformations may have made it a different tet.
    if ((badtet != (badface *) NULL) && !isdead(&badtet->tt)
         && org(badtet->tt) == badtet->forg
         && dest(badtet->tt) == badtet->fdest
         && apex(badtet->tt) == badtet->fapex
         && oppo(badtet->tt) == badtet->foppo) {
      if (b->verbose > 1) {
        printf("    Dequeuing btet (%d, %d, %d, %d).\n",
               pointmark(badtet->forg), pointmark(badtet->fdest),
               pointmark(badtet->fapex), pointmark(badtet->foppo));
      }
      // Create the new point p (at the circumcenter of t).
      makepoint(&newpt);
      for (i = 0; i < 3; i++) newpt[i] = badtet->cent[i];
      setpointtype(newpt, FREEVOLVERTEX);
      // Locate p.
      starttet = badtet->tt;
      loc = preciselocate(newpt, &starttet, tetrahedrons->items);
      if ((loc != ONVERTEX) && (loc != OUTSIDE)) {
        // For BC(p) and B(p).
        infect(starttet);
        tetlist->append(&starttet);
        formbowatcavityquad(newpt, tetlist, ceillist);
        // Check for encroached subsegments.
        reject = tallencsegs(newpt, 1, &ceillist);
        if (!reject) {
          // Check for encroached subfaces.
          reject = tallencsubs(newpt, 1, &ceillist);
        }
        // Execute point accepting rule if p does not encroach upon any
        //   subsegment and subface.
        if (!reject) {
          reject = !acceptvolpt(newpt, ceillist, verlist);
        }
        if (!reject) {
          reject = !trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist,
                                    &ceillist, -1.0);
        }
        if (!reject) {
          // BC(p) should include t, so that t can be removed after BC(p) is
          //   remeshed. However, if there are locally non-Delaunay faces
          //   and encroached subsegments/subfaces, t may not be collected
          //   in BC(p). p should not be inserted in such case.
          reject = !infected(badtet->tt);
          if (reject) outbowatcircumcount++;
        }
        if (!reject) {
          // Save a point for size interpolation.
          e1 = org(starttet);
          // Insert p.
          bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist,
                          NULL, NULL, false, false, true);
          setnewpointsize(newpt, e1, NULL);
          if (steinerleft > 0) steinerleft--;
        } else {
          // p is rejected for one of the following reasons:
          //   (1) BC(p) is not valid.
          //   (2) t does not in BC(p).
          //   (3) p encroaches upon some segments;
          //   (4) p encroaches upon some subfaces;
          //   (5) p is rejected by the point accepting rule.
          pointdealloc(newpt);
          // Uninfect tets of BC(p).
          for (i = 0; i < tetlist->len(); i++) {
            starttet = * (triface *)(* tetlist)[i];
            uninfect(starttet);
          }
        }
        tetlist->clear();
        ceillist->clear();
        // Split encroached subsegments/subfaces if there are.
        if (reject) {
          oldptnum = points->items;
          if (badsubsegs->items > 0) {
            repairencsegs(true, true);
          }
          if (badsubfaces->items > 0) {
            repairencsubs(true);
          }
          if (points->items > oldptnum) {
            // Some encroaching subsegments/subfaces got split. Re-queue the
            //   tet if it is still alive.
            starttet = badtet->tt;
            if (!isdead(&starttet)) {
              checktet4badqual(&starttet, true);
            }
          }
        }
      } else {
        // Do not insert p. The reason may be one of:
        //   (1) p is coincident (ONVERTEX) with an existing vertex; or
        //   (2) p is outside (OUTSIDE) the mesh.
        // Case (1) should not be possible. If such vertex v exists, it is
        //   the circumcenter of t, ie., t is non-Delaunay. Either t was got
        //   split before by v, but survived after v was inserted, or the
        //   same for a t' which is nearly co-spherical with t.  Whatsoever,
        //   there are encroached segments or subfaces by v but the routines
        //   tallencsegs() or tallencsubs() did not find them out.
        if (loc == ONVERTEX) {
          printf("Internal error in repairbadtets():\n");
          printf("  During repairing bad tet (%d, %d, %d, %d)\n",
                 pointmark(badtet->forg), pointmark(badtet->fdest),
                 pointmark(badtet->fapex), pointmark(badtet->foppo));
          printf("  New point %d is coincident with an existing vertex %d\n",
                 pointmark(newpt), pointmark(org(starttet)));
          internalerror();
        }
        // Case (2) can happen when there is a segment s (or subface f) which
        //   is close to f and is non-conforming Delaunay.  The circumcenter
        //   of t encroaches upon s (or f), but the circumcenter of s (or f)
        //   is rejected for insertion.
        pointdealloc(newpt);
      } // if ((loc != ONVERTEX) && (loc != OUTSIDE))
    } // if (!isdead(&badtet->tt) && org(badtet->tt) == badtet->forg &&
    // Remove the tet from the queue.
    dequeuebadtet();
  } // while ((badtetrahedrons->items > 0) && (steinerleft != 0))

  delete tetlist;
  delete ceillist;
  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enforcequality()    Refine the mesh.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enforcequality()
{
  long total, vertcount;
  int i;
  
  if (!b->quiet) {
    printf("Adding Steiner points to enforce quality.\n");
  } 

  total = vertcount = 0l;
  if (b->conformdel) {
    r2count = r3count = 0l;
  }

  // If both '-D' and '-r' options are used. 
  if (b->conformdel && b->refine) {
    markacutevertices(65.0);
  }
  // If '-m' is not used.
  if (!b->metric) {
    // Find and mark all sharp segments.
    marksharpsegments(65.0);
    // Decide the sizes for feature points.
    decidefeaturepointsizes();
  }

  // Initialize the pool of encroached subsegments.
  badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  // Looking for encroached subsegments.
  tallencsegs(NULL, 0, NULL);
  if (b->verbose && badsubsegs->items > 0) {
    printf("  Splitting encroached subsegments.\n");
  }
  vertcount = points->items;
  // Fix encroached segments without noting any enc subfaces.
  repairencsegs(false, false);
  if (b->verbose > 0) {
    printf("  %ld split points.\n", points->items - vertcount);
  }
  total += points->items - vertcount;

  // Initialize the pool of encroached subfaces.
  badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  // Initialize the priority queues of badfaces.
  for (i = 0; i < 3; i++) subquefront[i] = (badface *) NULL;
  for (i = 0; i < 3; i++) subquetail[i] = &subquefront[i];
  // Looking for encroached subfaces.
  tallencsubs(NULL, 0, NULL);
  if (b->verbose && badsubfaces->items > 0) {
    printf("  Splitting encroached subfaces.\n");
  }
  vertcount = points->items;
  // Fix encroached subfaces without noting bad tetrahedra.
  repairencsubs(false);
  if (b->verbose > 0) {
    printf("  %ld split points.\n", points->items - vertcount);
  }
  total += points->items - vertcount;
  // At this point, the mesh should be conforming Delaunay if no input
  //   angle is smaller than 90 degree.

  // Next, fix bad quality tetrahedra.
  if ((b->minratio > 0.0) || b->varvolume || b->fixedvolume) {
    // Initialize the pool of bad tets
    badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0);
    // Initialize the priority queues of bad tets.
    for (i = 0; i < 64; i++) tetquefront[i] = (badface *) NULL;
    firstnonemptyq = -1;
    recentq = -1;
    // Looking for bad quality tets.
    cosmaxdihed = cos(b->maxdihedral * PI / 180.0);
    cosmindihed = cos(b->mindihedral * PI / 180.0);
    tallbadtetrahedrons();
    if (b->verbose && badtetrahedrons->items > 0) {
      printf("  Splitting bad tetrahedra.\n");
    }
    vertcount = points->items;
    repairbadtets();
    if (b->verbose > 0) {
      printf("  %ld refinement points.\n", points->items - vertcount);
    }
    total += points->items - vertcount;
    delete badtetrahedrons;
  }

  if (b->verbose > 0) {
    printf("  Totally added %ld points.\n", total);
  }

  delete badsubfaces;
  delete badsubsegs;
}

//
// End of Delaunay refinement routines
//

//
// Begin of mesh optimization routines
//

void tetgenmesh::dumpbadtets()
{
  FILE *fout;
  badface *remtet;

  // Write out a file of remaining bad tets.
  printf("  Writing bad tets to file bad-dump.lua.\n");
  fout = fopen("bad-dump.lua", "w");
  fprintf(fout, "-- %ld remaining bad tets (> %g degree).\n",
          badtetrahedrons->items, b->maxdihedral);
  badtetrahedrons->traversalinit();
  remtet = badfacetraverse(badtetrahedrons);
  while (remtet != (badface *) NULL) {
    if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg &&
        dest(remtet->tt) == remtet->fdest && 
        apex(remtet->tt) == remtet->fapex &&
        oppo(remtet->tt) == remtet->foppo) {
      fprintf(fout, "p:draw_tet(%d, %d, %d, %d) -- %g\n",
              pointmark(remtet->forg), pointmark(remtet->fdest),
              pointmark(remtet->fapex), pointmark(remtet->foppo),
              acos(remtet->key) * 180.0 / PI);
    }
    remtet = badfacetraverse(badtetrahedrons);
  }
  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4ill()    Check a tet to see if it is illegal.                    //
//                                                                           //
// A tet is "illegal" if it spans on one input facet.  Save the tet in queue //
// if it is illegal and the flag 'enqflag' is set.                           //
//                                                                           //
// Note: Such case can happen when the input facet has non-coplanar vertices //
// and the Delaunay tetrahedralization of the vertices may creat such tets.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4ill(triface* testtet, bool enqflag)
{
  badface *newbadtet;
  triface checktet;
  face checksh1, checksh2;
  face checkseg;
  bool illflag;
  int i;

  illflag = false;
  for (testtet->loc = 0; testtet->loc < 4; testtet->loc++) {
    tspivot(*testtet, checksh1);
    if (checksh1.sh != dummysh) {
      testtet->ver = 0;
      findedge(&checksh1, org(*testtet), dest(*testtet));
      for (i = 0; i < 3; i++) {
        fnext(*testtet, checktet);
        tspivot(checktet, checksh2);
        if (checksh2.sh != dummysh) {
          // Two subfaces share this edge.
          sspivot(checksh1, checkseg);
          if (checkseg.sh == dummysh) {
            // The four corners of the tet are on one facet. Illegal! Try to
            //   flip the opposite edge of the current one.
            enextfnextself(*testtet);
            enextself(*testtet);
            illflag = true; 
            break;
          }
        }
        enextself(*testtet);
        senextself(checksh1);
      }
    }
    if (illflag) break;
  }

  if (illflag && enqflag) {
    // Allocate space for the bad tetrahedron.
    newbadtet = (badface *) badtetrahedrons->alloc();
    newbadtet->tt = *testtet;
    newbadtet->key = -1.0; // = 180 degree.
    for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0;
    newbadtet->forg = org(*testtet);
    newbadtet->fdest = dest(*testtet);
    newbadtet->fapex = apex(*testtet);
    newbadtet->foppo = oppo(*testtet);
    newbadtet->nextitem = (badface *) NULL;
    if (b->verbose > 2) {
      printf("    Queueing illtet: (%d, %d, %d, %d).\n",
             pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
             pointmark(newbadtet->fapex), pointmark(newbadtet->foppo));
    }
  }

  return illflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4opt()    Check a tet to see if it needs to be optimized.         //
//                                                                           //
// A tet t needs to be optimized if it fails to certain quality measures.    //
// The only quality measure currently used is the maximal dihedral angle at  //
// edges. The desired maximal dihedral angle is b->maxdihed (set by the '-s' //
// option.                                                                   //
//                                                                           //
// A tet may have one, two, or three big dihedral angles. Examples: Let the  //
// tet t = abcd, and its four corners are nearly co-planar. Then t has one   //
// big dihedral angle if d is very close to the edge ab; t has three big     //
// dihedral angles if d's projection on the face abc is also inside abc, i.e.//
// the shape of t likes a hat; finally, t has two big dihedral angles if d's //
// projection onto abc is outside abc.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag)
{
  badface *newbadtet;
  point pa, pb, pc, pd;
  REAL N[4][3], len;
  REAL cosd;
  bool enq;
  int i, j;

  enq = false;
  pa = (point) testtet->tet[4];
  pb = (point) testtet->tet[5];
  pc = (point) testtet->tet[6];
  pd = (point) testtet->tet[7];
  // Compute the 4 face normals: N[0] cbd, N[1] acd, N[2] bad, N[3] abc.
  tetallnormal(pa, pb, pc, pd, N, NULL);
  // Normalize the normals.
  for (i = 0; i < 4; i++) {
    len = sqrt(dot(N[i], N[i]));
    if (len != 0.0) {
      for (j = 0; j < 3; j++) N[i][j] /= len;
    }
  }
  // Find all large dihedral angles.
  for (i = 0; i < 6; i++) {
    // Locate the edge i and calculate the dihedral angle at the edge.
    testtet->loc = 0;
    testtet->ver = 0;
    switch (i) {
    case 0: // edge ab
      cosd = -dot(N[2], N[3]);
      break;
    case 1: // edge cd 
      enextfnextself(*testtet);
      enextself(*testtet);
      cosd = -dot(N[0], N[1]);
      break;
    case 2: // edge bd
      enextfnextself(*testtet);
      enext2self(*testtet);
      cosd = -dot(N[0], N[2]);
      break;
    case 3: // edge bc
      enextself(*testtet);
      cosd = -dot(N[0], N[3]);
      break;
    case 4: // edge ad
      enext2fnextself(*testtet);
      enextself(*testtet);
      cosd = -dot(N[1], N[2]);
      break;
    case 5: // edge ac
      enext2self(*testtet);
      cosd = -dot(N[1], N[3]);
      break;
    }
    if (cosd < cosmaxdihed) {
      // A bigger dihedral angle.
      if (enqflag) {
        // Allocate space for the bad tetrahedron.
        newbadtet = (badface *) badtetrahedrons->alloc();
        newbadtet->tt = *testtet;
        newbadtet->key = cosd;
        for (j = 0; j < 3; j++) newbadtet->cent[j] = 0.0;
        newbadtet->forg = org(*testtet);
        newbadtet->fdest = dest(*testtet);
        newbadtet->fapex = apex(*testtet);
        newbadtet->foppo = oppo(*testtet);
        newbadtet->nextitem = (badface *) NULL;
        if (b->verbose > 2) {
          printf("    Queueing tet: (%d, %d, %d, %d), dihed %g (degree).\n",
                 pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
                 pointmark(newbadtet->fapex), pointmark(newbadtet->foppo),
                 acos(cosd) * 180.0 / PI);
        }
      }
      enq = true;
    }
  }

  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedge()    Remove an edge                                            //
//                                                                           //
// 'remedge' is a tet (abcd) having the edge ab wanted to be removed.  Local //
// reconnecting operations are used to remove edge ab.  The following opera- //
// tion will be tryed.                                                       //
//                                                                           //
// If ab is on the hull, and abc and abd are both hull faces. Then ab can be //
// removed by stripping abcd from the mesh. However, if ab is a segemnt, do  //
// the operation only if 'b->optlevel' > 1 and 'b->nobisect == 0'.           //
//                                                                           //
// If ab is an internal edge, there are n tets contains it.  Then ab can be  //
// removed if there exists another m tets which can replace the n tets with- //
// out changing the boundary of the n tets.                                  //
//                                                                           //
// If 'optflag' is set.  The value 'remedge->key' means cos(theta), where    //
// 'theta' is the maximal dishedral angle at ab. In this case, even if the   //
// n-to-m flip exists, it will not be performed if the maximum dihedral of   //
// the new tets is larger than 'theta'.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedge(badface* remedge, bool optflag)
{
  triface abcd, badc;  // Tet configuration at edge ab.
  triface baccasing, abdcasing;
  triface abtetlist[11];  // Old configuration at ab, save maximum 10 tets.
  triface bftetlist[11];  // Old configuration at bf, save maximum 10 tets.
  triface newtetlist[33]; // New configuration after removing ab.
  face checksh;
  enum fliptype fty;
  REAL key;
  bool remflag, subflag;
  int n, n1, m, i, j;

  // First try to strip abcd from the mesh. This needs to check either ab
  //   or cd is on the hull. Try to strip it whichever is true.
  abcd = remedge->tt;
  adjustedgering(abcd, CCW);
  i = 0;
  do {
    sym(abcd, baccasing);
    // Is the tet on the hull?
    if (baccasing.tet == dummytet) {
      fnext(abcd, badc);
      sym(badc, abdcasing);
      if (abdcasing.tet == dummytet) {
        // Strip the tet from the mesh -> ab is removed as well.
        if (removetetbypeeloff(&abcd)) {
          if (b->verbose > 1) {
            printf("    Stripped tet from the mesh.\n");
          }
          optcount[0]++;
          return true;
        }
      }
    }
    // Check if the oppsite edge cd is on the hull.
    enext2fnextself(abcd);
    enext2self(abcd);
    esymself(abcd); // --> cdab
    i++;
  } while (i < 2);
  
  // Get the tets configuration at ab. Collect maximum 10 tets.
  subflag = false;
  abcd = remedge->tt;
  adjustedgering(abcd, CW);
  n = 0;
  abtetlist[n] = abcd;
  do {
    // Is the list full?
    if (n == 10) break;
    // Stop if a subface appears.
    tspivot(abtetlist[n], checksh);
    if (checksh.sh != dummysh) {
      // ab is either a segment or a facet edge. The latter case is not
      //   handled yet! An edge flip is needed.
      subflag = true; break; // return false;
    }
    // Get the next tet at ab.
    fnext(abtetlist[n], abtetlist[n + 1]);
    n++;
  } while (apex(abtetlist[n]) != apex(abcd));

  remflag = false;
  key = remedge->key;

  if (subflag && optflag) {
    abcd = remedge->tt;
    adjustedgering(abcd, CCW);
    // Try to flip face cda or cdb to improve quality.
    for (j = 0; j < 2; j++) {
      if (j == 0) {
        enext2fnext(abcd, abtetlist[0]); // Goto cda.
      } else {
        enextfnext(abcd, abtetlist[0]); // Goto cdb.
      }
      fty = categorizeface(abtetlist[0]);
      if (fty == T23) {
        // A 2-to-3 flip is possible.
        sym(abtetlist[0], abtetlist[1]);
        assert(abtetlist[1].tet != dummytet);
        n = 2; 
        m = 3;
        remflag = removefacebyflip23(&key, abtetlist, newtetlist, NULL);
      } else if (fty == T22) {
        // A 2-to-2 or 4-to-4 flip is possible.
        n = 2;
        newtetlist[0] = abtetlist[0];
        adjustedgering(newtetlist[0], CW);
        fnext(newtetlist[0], newtetlist[1]);
        assert(newtetlist[1].tet != dummytet);
        // May it is 4-to-4 flip.
        if (fnext(newtetlist[1], newtetlist[2])) {
          fnext(newtetlist[2], newtetlist[3]);
          assert(newtetlist[3].tet != dummytet);
          n = 4;
        }
        m = n;
        remflag = removeedgebyflip22(&key, n, newtetlist, NULL);
      }
      // Has quality been improved?
      if (remflag) {
        if (b->verbose > 1) {
          printf("  Done flip %d-to-%d. Qual: %g -> %g.\n", n, m,
                 acos(remedge->key) / PI * 180.0, acos(key) / PI * 180.0);
        }
        // Delete the old tets. Note, flip22() does not create new tets.
        if (m == 3) {
          for (i = 0; i < n; i++) {
            tetrahedrondealloc(abtetlist[i].tet);
          }
        }
        for (i = 0; i < m; i++) {
          checktet4opt(&(newtetlist[i]), true);
        }
        optcount[1]++;
        return true;
      }
    } // if (j = 0; j < 2; j++)
    // Faces are not flipable. Return.
    return false;
  }

  // 2 <= n <= 10.
  if (n == 3) {
    // There are three tets at ab. Try to do a flip32 at ab.
    remflag = removeedgebyflip32(&key, abtetlist, newtetlist, NULL);
  } else if ((n == 4) || (n == 5) || (n == 6)) {
    // Four tets case. Try to do edge transformation.
    remflag = removeedgebytranNM(&key,n,abtetlist,newtetlist,NULL,NULL,NULL);
  } else {
    if (b->verbose > 1) {
      printf("  !! Unhandled case: n = %d.\n", n);
    }
  }
  if (remflag) {
    optcount[n]++;
    // Delete the old tets.
    for (i = 0; i < n; i++) {
      tetrahedrondealloc(abtetlist[i].tet);
    }
    m = (n - 2) * 2; // The numebr of new tets.
    if (b->verbose > 1) {
      printf("  Done flip %d-to-%d. ", n, m);
      if (optflag) {
        printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0,
               acos(key) / PI * 180.0);
      }
      printf("\n");
    }
  } 

  if (!remflag && (key == remedge->key) && (n < 7)) {
    // Try to do a combination of flips.
    n1 = 0;
    remflag = removeedgebycombNM(&key, n, abtetlist, &n1, bftetlist,
      newtetlist, NULL);
    if (remflag) {
      optcount[9]++;
      // Delete the old tets.
      for (i = 0; i < n; i++) {
        tetrahedrondealloc(abtetlist[i].tet);
      }
      for (i = 0; i < n1; i++) {
        if (!isdead(&(bftetlist[i]))) {
          tetrahedrondealloc(bftetlist[i].tet);
        }
      }
      m = ((n1 - 2) * 2 - 1) + (n - 3) * 2; // The number of new tets.
      if (b->verbose > 1) {
        printf("  Done flip %d-to-%d (n-1=%d, n1=%d). ", n+n1-2, m, n-1,n1);
        if (optflag) {
          printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0,
               acos(key) / PI * 180.0);
        }
        printf("\n");
      }
    }
  }

  if (remflag) {
    // edge is removed. Test new tets for further optimization.
    for (i = 0; i < m; i++) {
      if (optflag) {
        checktet4opt(&(newtetlist[i]), true);
      } else {
        checktet4ill(&(newtetlist[i]), true);
      }
    }
  }

  return remflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// smoothsliver()    Remove a sliver by smoothing a vertex of it.            //
//                                                                           //
// The 'slivtet' represents a sliver abcd, and ab is the current edge which  //
// has a large dihedral angle (close to 180 degree).                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::smoothsliver(badface* remedge, list *starlist)
{
  triface checktet;
  point smthpt;
  bool smthed;
  int idx, i, j;

  // Find a Steiner volume point and smooth it.
  smthed = false;
  for (i = 0; i < 4 && !smthed; i++) {
    smthpt = (point) remedge->tt.tet[4 + i];
    // Is it a volume point?
    if (pointtype(smthpt) == FREEVOLVERTEX) {
      // Is it a Steiner point?
      idx = pointmark(smthpt) - in->firstnumber;
      if (!(idx < in->numberofpoints)) {
        // Smooth a Steiner volume point.
        starlist->append(&(remedge->tt.tet));
        formstarpolyhedron(smthpt, starlist, NULL, false);
        smthed = smoothpoint(smthpt,NULL,NULL,starlist,false,&remedge->key);
        // If it is smoothed. Queue new bad tets.
        if (smthed) {
          for (j = 0; j < starlist->len(); j++) {
            checktet = * (triface *)(* starlist)[j];
            checktet4opt(&checktet, true);
          }
        }
        starlist->clear();
      }
    }
  } 

  /* Omit to smooth segment points. This may cause infinite loop.
  if (smthed) {
    return true;
  }
  face abseg, nextseg, prevseg;
  point pt[2];
  // Check if ab is a segment.
  tsspivot(slivtet, &abseg);
  if (abseg.sh == dummysh) {
    // ab is not a segment. Check if a or b is a Steiner segment point.
    for (i = 0; i < 2 && !smthed; i++) {
      smthpt = (i == 0 ? org(*slivtet) : dest(*slivtet));
      if (pointtype(smthpt) == FREESEGVERTEX) {
        // Is it a Steiner point?
        idx = pointmark(smthpt) - in->firstnumber;
        if (!(idx < in->numberofpoints)) {
          // Smooth a Steiner segment point. Get the segment.
          sdecode(point2sh(smthpt), nextseg);
          locateseg(smthpt, &nextseg);
          assert(sorg(nextseg) == smthpt);
          pt[0] = sdest(nextseg);
          senext2(nextseg, prevseg);
          spivotself(prevseg);
          prevseg.shver = 0;
          if (sorg(prevseg) == smthpt) sesymself(prevseg);
          assert(sdest(prevseg) == smthpt);
          pt[1] = sorg(prevseg);
          starlist->append(slivtet);
          formstarpolyhedron(smthpt, starlist, NULL, true);
          smthed = smoothpoint(smthpt, pt[0], pt[1], starlist, false);
          // If it is smoothed. Check if the tet is still a sliver.
          if (smthed) checktet4opt(slivtet, true);
          starlist->clear();
        }
      }
    }
  }
  */

  return smthed;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsliver()    Remove a sliver by inserting a point.                    //
//                                                                           //
// The 'remedge->tt' represents a sliver abcd, ab is the current edge which  //
// has a large dihedral angle (close to 180 degree).                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist)
{
  triface starttet;
  face checkseg;
  point newpt, pt[4];
  bool remflag;
  int i;

  starttet = remedge->tt;

  // Check if cd is a segment.
  adjustedgering(starttet, CCW);
  enextfnextself(starttet);
  enextself(starttet);
  tsspivot(&starttet, &checkseg);
  if (b->nobisect == 0) {
    if (checkseg.sh != dummysh) {
      // cd is a segment. The seg will be split. BUT do not flip! Due to the
      //   exact predicates, lot of slivers ay be rsulted and hard to remove.
      checkseg.shver = 0;
      pt[0] = sorg(checkseg);
      pt[1] = sdest(checkseg);
      makepoint(&newpt);
      getsplitpoint(pt[0], pt[1], NULL, newpt);
      setpointtype(newpt, FREESEGVERTEX);
      setpoint2sh(newpt, sencode(checkseg));
      // Insert p, this should always success.
      sstpivot(&checkseg, &starttet);
      splittetedge(newpt, &starttet, NULL);
      // Collect the new tets connecting at p.
      sstpivot(&checkseg, &starttet);
      ceillist->append(&starttet);
      formstarpolyhedron(newpt, ceillist, NULL, true);
      setnewpointsize(newpt, pt[0], NULL);
      if (steinerleft > 0) steinerleft--;
      // Smooth p.
      smoothpoint(newpt, pt[0], pt[1], ceillist, false, NULL);
      // Queue new slivers.
      for (i = 0; i < ceillist->len(); i++) {
        starttet = * (triface *)(* ceillist)[i];
        checktet4opt(&starttet, true);
      }
      ceillist->clear();
      return true;
    }
  }

  // Get the four corners.
  for (i = 0; i < 4; i++) {
    pt[i] = (point) starttet.tet[4 + i];
  }
  // Create the new point p (at the circumcenter of t).
  makepoint(&newpt);
  for (i = 0; i < 3; i++) {
    newpt[i] = 0.25 * (pt[0][i] + pt[1][i] + pt[2][i] + pt[3][i]);
  }
  setpointtype(newpt, FREEVOLVERTEX);

  // Form the Bowyer-Watson cavity of p.
  remflag = false;
  infect(starttet);
  tetlist->append(&starttet);
  formbowatcavityquad(newpt, tetlist, ceillist);
  if (trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, -1.0)) {
    // Smooth p.
    if (smoothpoint( newpt, NULL, NULL, ceillist, false, &remedge->key)) {
      // Insert p.
      bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, NULL,
                      NULL, false, false, false);
      setnewpointsize(newpt, pt[0], NULL);
      if (steinerleft > 0) steinerleft--;
      // Queue new slivers.
      for (i = 0; i < ceillist->len(); i++) {
        starttet = * (triface *)(* ceillist)[i];
        checktet4opt(&starttet, true);
      }
      remflag = true;
    } // if (smoothpoint) 
  } // if (trimbowatcavity) 

  if (!remflag) {
    // p is rejected for BC(p) is not valid.
    pointdealloc(newpt);
    // Uninfect tets of BC(p).
    for (i = 0; i < tetlist->len(); i++) {
      starttet = * (triface *)(* tetlist)[i];
      uninfect(starttet);
    }
  }
  tetlist->clear();
  ceillist->clear();

  return remflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallslivers()    Queue all the slivers in the mesh.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallslivers(bool optflag)
{
  triface tetloop;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (optflag) {
      checktet4opt(&tetloop, true);
    } else {
      checktet4ill(&tetloop, true);
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// optimizemesh()    Improve mesh quality by mesh optimizations.             //
//                                                                           //
// Available mesh optimizing operations are: (1) multiple edge flips (3-to-2,//
// 4-to-4, 5-to-6, etc), (2) free vertex deletion, (3) new vertex insertion. //
// (1) is mandatory, while (2) and (3) are optionally.                       //
//                                                                           //
// The variable 'b->optlevel' (set after '-s') determines the use of these   //
// operations. If it is: 0, do no optimization; 1, only do (1) operation; 2, //
// do (1) and (2) operations; 3, do all operations. Deault, b->optlvel = 2.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::optimizemesh(bool optflag)
{
  list *splittetlist, *tetlist, *ceillist;
  badface *remtet, *lastentry;
  REAL maxdihed, objdihed, curdihed;
  long oldnum;
  int iter, i;

  if (!b->quiet) {
    if (optflag) {
      printf("Optimizing mesh.\n");
    } else {
      printf("Repairing mesh.\n");
    }
  }

#ifdef SELF_CHECK
  if (optflag && (b->verbose)) {
    printf("  level = %d.\n", b->optlevel);
  }
#endif

  // Initialize the pool of bad tets.
  badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0);
  if (optflag) {
    cosmaxdihed = cos(b->maxdihedral * PI / 180.0);
    cosmindihed = cos(b->mindihedral * PI / 180.0);
    // The radian of the maximum dihedral angle.
    maxdihed = b->maxdihedral / 180.0 * PI;
    // A sliver has an angle large than 'objdihed' will be split.
    objdihed = b->maxdihedral + 5.0;
    if (objdihed < 170.0) objdihed = 170.0;
    objdihed = objdihed / 180.0 * PI;
  }
  // Looking for non-optimal tets.
  tallslivers(optflag);

  optcount[0] = 0l;  // tet strip count.
  optcount[1] = 0l;  // face (2-3) and edge (2-2) flip count.
  optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips.
  optcount[9] = 0l;  // combined flip count.

  // Perform edge flip to improve quality.
  lastentry = (badface *) NULL;
  // Loop until pool 'badtetrahedrons' is empty.
  while (badtetrahedrons->items > 0) {
    badtetrahedrons->traversalinit();
    remtet = badfacetraverse(badtetrahedrons);
    while (remtet != (badface *) NULL) {
      // Make sure that the tet is still the same one when it was tested.
      //   Subsequent transformations may have made it a different tet.
      if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg &&
          dest(remtet->tt) == remtet->fdest && 
          apex(remtet->tt) == remtet->fapex &&
          oppo(remtet->tt) == remtet->foppo) {
        if (b->verbose > 1) {
          printf("    Repair tet (%d, %d, %d, %d) %g (degree).\n",
                 pointmark(remtet->forg), pointmark(remtet->fdest),
                 pointmark(remtet->fapex), pointmark(remtet->foppo),
                 acos(remtet->key) / PI * 180.0);
        }
        if (!removeedge(remtet, optflag)) {
          // An unremoveable tet. Check if it forms a loop.
          if (lastentry != (badface *) NULL) {
            if (remtet == lastentry) break;
          } else {
            // Remember this tet as a breakpoint.
            lastentry = remtet;
          }
        } else {
          // Clear the breakpoint.
          lastentry = (badface *) NULL;
          // Remove the entry from the queue.
          badfacedealloc(badtetrahedrons, remtet);
        }
      } else {
        // Remove the entry from the queue.
        badfacedealloc(badtetrahedrons, remtet);
      }
      remtet = badfacetraverse(badtetrahedrons);
    }
    // Stop if the above loop was out by force.
    if (remtet != (badface *) NULL) break;
  }

  if (b->verbose) {
    if (optcount[0] > 0l) {
      printf("  %ld tets are peeled off.\n", optcount[0]);
    }
    if (optcount[1] > 0l) {
      printf("  %ld faces are flipped.\n", optcount[1]);
    }
    if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + 
        optcount[9] > 0l) {
      printf("  %ld edges are flipped.\n", optcount[3] + optcount[4] +
             optcount[5] + optcount[6] + optcount[9]);
    }
    // if (badtetrahedrons->items > 0l) {
    //   printf("  %ld edges remain.\n", badtetrahedrons->items);
    // }
  }

  if ((badtetrahedrons->items > 0l) && optflag  && (b->optlevel > 2)) {
    splittetlist = new list(sizeof(badface), NULL, 256);
    tetlist = new list(sizeof(triface), NULL, 256);
    ceillist = new list(sizeof(triface), NULL, 256);
    oldnum = points->items;
    smoothsegverts = smoothvolverts = 0;
    optcount[1] = 0l;
    optcount[3] = optcount[4] = optcount[5] = optcount[6] = 0l; // edge flips.
    optcount[9] = 0l;  // combined flip count.
    iter = 0;

    do {
      // Form a list of slivers to be split and clean the pool.
      badtetrahedrons->traversalinit();
      remtet = badfacetraverse(badtetrahedrons);
      while (remtet != (badface *) NULL) {
        splittetlist->append(remtet);
        // Remove the entry from the queue.
        badfacedealloc(badtetrahedrons, remtet);
        remtet = badfacetraverse(badtetrahedrons);
      }
      for (i = 0; i < splittetlist->len(); i++) {
        remtet = (badface *)(* splittetlist)[i];
        // Make sure that the tet is still the same one when it was tested.
        //   Subsequent transformations may have made it a different tet.
        if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg &&
            dest(remtet->tt) == remtet->fdest && 
            apex(remtet->tt) == remtet->fapex &&
            oppo(remtet->tt) == remtet->foppo) {
          // The sliver may get smoothed due to a neighboring tet.
          curdihed = facedihedral(remtet->forg, remtet->fdest, remtet->fapex,
                                  remtet->foppo);
          // The dihedral angle of a tet must less than PI, correct it.
          if (curdihed > PI) curdihed = 2 * PI - curdihed;
          // Is it a large angle?
          if (curdihed > objdihed) {
            remtet->key = cos(curdihed);
            if (b->verbose > 1) {
              printf("    Get sliver (%d, %d, %d, %d) %g (degree).\n",
                     pointmark(remtet->forg), pointmark(remtet->fdest),
                     pointmark(remtet->fapex), pointmark(remtet->foppo),
                     acos(remtet->key) / PI * 180.0);
            }
            if (!removeedge(remtet, optflag)) {
              if (!smoothsliver(remtet, tetlist)) {
                splitsliver(remtet, tetlist, ceillist);
              }
            }
          }
        }
      }
      iter++;
    } while ((badtetrahedrons->items > 0l) && (iter < b->optpasses));
    
    if (b->verbose) {
      printf("  %d passes.\n", iter);
      if ((points->items - oldnum) > 0l) {
        printf("  %ld points are inserted (%d on segment).\n",
               points->items - oldnum, smoothsegverts);
      }
      if (optcount[1] > 0l) {
        printf("  %ld faces are flipped.\n", optcount[1]);
      }
      if (optcount[3] + optcount[4] + optcount[5] + optcount[6] + 
          optcount[9] > 0l) {
        printf("  %ld edges are flipped.\n", optcount[3] + optcount[4] +
               optcount[5] + optcount[6] + optcount[9]);
      }
      // if (badtetrahedrons->items > 0l) {
      //   printf("  %ld edges remain.\n", badtetrahedrons->items);
      // }
    }
    delete tetlist;
    delete ceillist;
    delete splittetlist;
  }

  delete badtetrahedrons;
  badtetrahedrons = (memorypool *) NULL;
}

//
// End of mesh optimization routines
//

//
// Begin of I/O rouitnes
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// transfernodes()    Transfer nodes from 'io->pointlist' to 'this->points'. //
//                                                                           //
// Initializing 'this->points'.  Transferring all points from 'in->pointlist'//
// into it. All points are indexed (start from in->firstnumber).  Each point //
// is initialized be UNUSEDVERTEX.  The bounding box (xmin, xmax, ymin, ymax,//
// zmin, zmax) and the diameter (longest) of the point set are calculated.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::transfernodes()
{
  point pointloop;
  REAL x, y, z;
  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);
    // 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.
    for (j = 0; j < in->numberofpointattributes; j++) {
      pointloop[3 + j] = in->pointattributelist[attribindex++];
    }
    // Read the point metric tensor.
    for (j = 0; j < in->numberofpointmtrs; j++) {
      pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
    }
    // Determine the smallest and largests x, y and z coordinates.
    if (i == 0) {
      xmin = xmax = x;
      ymin = ymax = y;
      zmin = zmax = z;
    } else {
      xmin = (x < xmin) ? x : xmin;
      xmax = (x > xmax) ? x : xmax;
      ymin = (y < ymin) ? y : ymin;
      ymax = (y > ymax) ? y : ymax;
      zmin = (z < zmin) ? z : zmin;
      zmax = (z > zmax) ? z : zmax;
    }
  }
  // 'longest' is the largest possible edge length formed by input vertices.
  x = xmax - xmin;
  y = ymax - ymin;
  z = zmax - zmin;
  longest = sqrt(x * x + y * y + z * z);
  if (longest == 0.0) {
    printf("Error:  The point set is trivial.\n");
    terminatetetgen(1);
  }
  // Two identical points are distinguished by 'lengthlimit'.
  lengthlimit = longest * b->epsilon * 1e+2;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// jettisonnodes()    Jettison unused or duplicated vertices.                //
//                                                                           //
// Unused points are those input points which are outside the mesh domain or //
// have no connection (isolated) to the mesh.  Duplicated points exist for   //
// example if the input PLC is read from a .stl mesh file (marked during the //
// Delaunay tetrahedralization step. This routine remove these points from   //
// points list. All existing points are reindexed.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::jettisonnodes()
{
  point pointloop;
  bool jetflag;
  int oldidx, newidx;
  int remcount;

  if (!b->quiet) {
    printf("Jettisoning redundants points.\n");
  }

  points->traversalinit();
  pointloop = pointtraverse();
  oldidx = newidx = 0; // in->firstnumber;
  remcount = 0;
  while (pointloop != (point) NULL) {
    jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || 
      (pointtype(pointloop) == UNUSEDVERTEX);
    if (jetflag) {
      // It is a duplicated 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++;
    if (oldidx == in->numberofpoints) {
      // Update the numbe of input points (Because some were removed).
      in->numberofpoints -= remcount;
      // Remember this number for output original input nodes.
      jettisoninverts = remcount;
    }
    pointloop = pointtraverse();
  }
  if (b->verbose) {
    printf("  %d duplicated vertices have been removed.\n", dupverts);
    printf("  %d unused vertices have been removed.\n", unuverts);
  }
  dupverts = 0;
  unuverts = 0;

  // 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;
  triface spintet, adjtet;
  point torg, tdest, tapex;
  point *extralist, *adjextralist;
  point newpoint;
  int hitbdry, ptmark;
  int i, j;

  if (!b->quiet) {
    printf("Adding vertices for second-order tetrahedra.\n");
  }

  // Initialize the 'highordertable'.
  highordertable = new point[tetrahedrons->items * 6];
  if (highordertable == (point *) NULL) {
    printf("Error:  Out of memory.\n");
    terminatetetgen(1);
  }

  // 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) {
        // Operate on this edge.
        worktet.loc = edge2locver[i][0];
        worktet.ver = edge2locver[i][1];
        // Create a new node on this edge.
        torg = org(worktet);
        tdest = dest(worktet);
        // Create a new node in the middle of the edge.
        newpoint = (point) points->alloc();
        // Interpolate its attributes.
        for (j = 0; j < 3 + in->numberofpointattributes; j++) {
          newpoint[j] = 0.5 * (torg[j] + tdest[j]);
        }
        ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
        setpointmark(newpoint, ptmark);
        // Add this node to its extra node list.
        extralist[i] = newpoint;
        // Set 'newpoint' into extra node lists of other tetrahedra
        //   sharing this edge.
        tapex = apex(worktet);
        spintet = worktet;
        hitbdry = 0;
        while (hitbdry < 2) {
          if (fnextself(spintet)) {
            // Get the extra node list of 'spintet'.
            adjextralist = (point *) spintet.tet[highorderindex];
            // Find the index of its extra node list.
            j = locver2edge[spintet.loc][spintet.ver];
            // Only set 'newpoint' into 'adjextralist' if it is a NULL.
            //   Because two faces can belong to the same tetrahedron.
            if (adjextralist[j] == (point) NULL) {
              adjextralist[j] = newpoint;
            }
            if (apex(spintet) == tapex) {
              break;
            }
          } else {
            hitbdry++;
            if (hitbdry < 2) {
              esym(worktet, spintet);
	    }
          }
        }
      }
    }
    tetloop.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;
  char outnodefilename[FILENAMESIZE];
  shellface subptr;
  triface adjtet;
  face subloop;
  point pointloop;
  point *extralist, ep[3];
  int nextras, bmark, shmark, marker;
  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 = in->numberofpointattributes;
  bmark = !b->nobound && in->pointmarkerlist;

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  marker = coordindex = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outnodefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outnodefilename);
      terminatetetgen(1);
    }
    // Number of points, number of dimensions, number of point attributes,
    //   and number of boundary markers (zero or one).
    fprintf(outfile, "%ld  %d  %d  %d\n", points->items, 3, nextras, bmark);
  } else {
    // Allocate space for 'pointlist';
    out->pointlist = new REAL[points->items * 3];
    if (out->pointlist == (REAL *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    // Allocate space for 'pointattributelist' if necessary;
    if (nextras > 0) {
      out->pointattributelist = new REAL[points->items * nextras];
      if (out->pointattributelist == (REAL *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    // Allocate space for 'pointmarkerlist' if necessary;
    if (bmark) {
      out->pointmarkerlist = new int[points->items];
      if (out->pointmarkerlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberofpoints = points->items;
    out->numberofpointattributes = nextras;
    coordindex = 0;
    attribindex = 0;
  }

  if (bmark && (b->plc || b->refine)) {
    // Initialize the point2tet field of each point.
    points->traversalinit();
    pointloop = pointtraverse();
    while (pointloop != (point) NULL) {
      setpoint2tet(pointloop, (tetrahedron) NULL);
      pointloop = pointtraverse();
    }
    // Make a map point-to-subface. Hence a boundary point will get the
    //   facet marker from that facet where it lies on.
    subfaces->traversalinit();
    subloop.sh = shellfacetraverse(subfaces);
    while (subloop.sh != (shellface *) NULL) {
      subloop.shver = 0;
      // Check all three points of the subface.
      for (i = 0; i < 3; i++) {
        pointloop = (point) subloop.sh[3 + i];
        setpoint2tet(pointloop, (tetrahedron) sencode(subloop));
      }
      if (b->order == 2) {
        // '-o2' switch. Set markers for quadratic nodes of this subface.
        stpivot(subloop, adjtet);
        if (adjtet.tet == dummytet) {
          sesymself(subloop);
          stpivot(subloop, adjtet);
        }
        assert(adjtet.tet != dummytet);
        extralist = (point *) adjtet.tet[highorderindex];
        switch (adjtet.loc) {
        case 0:
          ep[0] = extralist[0];
          ep[1] = extralist[1];
          ep[2] = extralist[2];
          break;
        case 1:
          ep[0] = extralist[0];
          ep[1] = extralist[4];
          ep[2] = extralist[3];
          break;
        case 2:
          ep[0] = extralist[1];
          ep[1] = extralist[5];
          ep[2] = extralist[4];
          break;
        case 3:
          ep[0] = extralist[2];
          ep[1] = extralist[3];
          ep[2] = extralist[5];
          break;
        default: break;
        }
        for (i = 0; i < 3; i++) {
          setpoint2tet(ep[i], (tetrahedron) sencode(subloop));
        }
      }
      subloop.sh = shellfacetraverse(subfaces);
    }
  }

  // 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];
      }
      // Is it a boundary vertex has marker zero?
      if ((marker == 0) && (b->plc || b->refine)) {
        subptr = (shellface) point2tet(pointloop);
        if (subptr != (shellface) NULL) {
          // Default a boundary vertex has marker 1.
          marker = 1;
          if (in->facetmarkerlist != (int *) NULL) {
            // The vertex gets the marker from the facet it lies on.
            sdecode(subptr, subloop);
            shmark = shellmark(subloop);
            marker = in->facetmarkerlist[shmark - 1];
          }
        }
      }
    }
    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.
        fprintf(outfile, "  %.17g", pointloop[3 + i]);
      }
      if (bmark) {
        // Write the boundary marker.
        fprintf(outfile, "    %d", marker);
      }
      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.
        out->pointattributelist[attribindex++] = pointloop[3 + i];
      }
      if (bmark) {
        // Output the boundary marker.  
        out->pointmarkerlist[index] = marker;
      }
    }
    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;
  char outmtrfilename[FILENAMESIZE];
  list *tetlist, *ptlist;
  triface tetloop;
  point ptloop, neipt;
  REAL lave, len; // lmin, lmax, 
  int mtrindex;
  int i;  

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  mtrindex = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outmtrfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outmtrfilename);
      terminatetetgen(1);
    }
    // Number of points, number of point metrices,
    // fprintf(outfile, "%ld  %d\n", points->items, sizeoftensor + 3);
    fprintf(outfile, "%ld  %d\n", points->items, 1);
  } else {
    // Allocate space for 'pointmtrlist' if necessary;
    // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)];
    out->pointmtrlist = new REAL[points->items];
    if (out->pointmtrlist == (REAL *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    out->numberofpointmtrs = 1; // (sizeoftensor + 3);
    mtrindex = 0;
  }
  
  // Initialize the point2tet field of each point.
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    setpoint2tet(ptloop, (tetrahedron) NULL);
    ptloop = pointtraverse();
  }
  // Create the point-to-tet map.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    for (i = 0; i < 4; i++) {
      ptloop = (point) tetloop.tet[4 + i];
      setpoint2tet(ptloop, encode(tetloop));
    }
    tetloop.tet = tetrahedrontraverse();
  }

  tetlist = new list(sizeof(triface), NULL, 256);
  ptlist = new list(sizeof(point *), NULL, 256);

  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    decode(point2tet(ptloop), tetloop);
    if (!isdead(&tetloop)) {
      // Form the star of p.
      tetlist->append(&tetloop);
      formstarpolyhedron(ptloop, tetlist, ptlist, true);
      // lmin = longest;
      // lmax = 0.0;
      lave = 0.0;
      for (i = 0; i < ptlist->len(); i++) {
        neipt = * (point *)(* ptlist)[i];
        len = distance(ptloop, neipt);
        // lmin = lmin < len ? lmin : len;
        // lmax = lmax > len ? lmax : len;
        lave += len;
      }
      lave /= ptlist->len();
    }
    if (out == (tetgenio *) NULL) {
      // for (i = 0; i < sizeoftensor; i++) {
      //   fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]);
      // }
      if (ptlist->len() > 0) {
        // fprintf(outfile, "%-16.8e %-16.8e %-16.8e", lmin, lmax, lave);
        fprintf(outfile, "%-16.8e ", lave);
      } else {
        fprintf(outfile, "0.0 "); // fprintf(outfile, "0.0  0.0  0.0");
      }
      fprintf(outfile, "\n");
    } else {
      // for (i = 0; i < sizeoftensor; i++) {
      //   out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
      // }
      if (ptlist->len() > 0) {
        // out->pointmtrlist[mtrindex++] = lmin;
        // out->pointmtrlist[mtrindex++] = lmax;
        out->pointmtrlist[mtrindex++] = lave;
      } else {
        // out->pointmtrlist[mtrindex++] = 0.0;
        // out->pointmtrlist[mtrindex++] = 0.0;
        out->pointmtrlist[mtrindex++] = 0.0;
      }
    }
    tetlist->clear();
    ptlist->clear();
    ptloop = pointtraverse();
  }

  delete tetlist;
  delete ptlist;

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

void tetgenmesh::outelements(tetgenio* out)
{
  FILE *outfile;
  char outelefilename[FILENAMESIZE];
  tetrahedron* tptr;
  int *tlist;
  REAL *talist;
  int firstindex, shift;
  int pointindex;
  int attribindex;
  point p1, p2, p3, p4;
  point *extralist;
  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");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  tlist = (int *) NULL;
  talist = (double *) NULL;
  pointindex = attribindex = 0;

  eextras = in->numberoftetrahedronattributes;
  if (out == (tetgenio *) NULL) {
    outfile = fopen(outelefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outelefilename);
      terminatetetgen(1);
    }
    // Number of tetras, points per tetra, attributes per tetra.
    fprintf(outfile, "%ld  %d  %d\n", tetrahedrons->items,
            b->order == 1 ? 4 : 10, eextras);
  } else {
    // Allocate memory for output tetrahedra.
    out->tetrahedronlist = new int[tetrahedrons->items * 
                                   (b->order == 1 ? 4 : 10)];
    if (out->tetrahedronlist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    // Allocate memory for output tetrahedron attributes if necessary.
    if (eextras > 0) {
      out->tetrahedronattributelist = new REAL[tetrahedrons->items * eextras];
      if (out->tetrahedronattributelist == (REAL *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberoftetrahedra = tetrahedrons->items;
    out->numberofcorners = b->order == 1 ? 4 : 10;
    out->numberoftetrahedronattributes = eextras;
    tlist = out->tetrahedronlist;
    talist = out->tetrahedronattributelist;
    pointindex = 0;
    attribindex = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tptr = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tptr != (tetrahedron *) NULL) {
    p1 = (point) tptr[4];
    p2 = (point) tptr[5];
    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];
        // Tetrahedron number, indices for four points plus 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);
      }
    }
    if (b->neighout) {
      // Remember the index of this element.
      * (int *) (tptr + elemmarkerindex) = elementnumber;
    }
    tptr = tetrahedrontraverse();
    elementnumber++;
  }
  if (b->neighout) {
    // Set the outside element marker.
    * (int *) (dummytet + elemmarkerindex) = -1;
  }

  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 structure.   //
//                                                                           //
// This routines outputs all triangular faces (including outer boundary      //
// faces and inner faces) of this mesh.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outfaces(tetgenio* out)
{
  FILE *outfile;
  char facefilename[FILENAMESIZE];
  int *elist;
  int *emlist;
  int neigh1, neigh2;
  int index;
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  long faces;
  int bmark, faceid, marker;
  int firstindex, shift;
  int facenumber;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index =  marker = 0;

  faces = (4l * tetrahedrons->items + hullsize) / 2l;
  bmark = !b->nobound && in->facetmarkerlist;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(1);
    }
    fprintf(outfile, "%ld  %d\n", faces, bmark);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[faces * 3];
    if (out->trifacelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    // Allocate memory for 'trifacemarkerlist' if necessary.
    if (bmark) {
      out->trifacemarkerlist = new int[faces];
      if (out->trifacemarkerlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[subfaces->items * 2];
      if (out->adjtetlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = faces;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
    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();
  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 there isn't another tetrahedron
  //   adjacent to this face, operate on the face.  If there is another
  //   adjacent tetrahedron, operate on the face only if the current
  //   tetrahedron has a smaller pointer than its neighbor.  This way, each
  //   face is considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tsymface.tet == dummytet) || (tface.tet < tsymface.tet)) {
        torg = org(tface);
        tdest = dest(tface);
        tapex = apex(tface);
        if (bmark) {
          // Get the boundary marker of this face. If it is an inner face,
          //   it has no boundary marker, set it be zero.
          if (b->useshelles) {
            // Shell face is used.
            tspivot(tface, checkmark);
            if (checkmark.sh == dummysh) {
              marker = 0;  // It is an inner face.
            } else {
              faceid = shellmark(checkmark) - 1;
              marker = in->facetmarkerlist[faceid];
            }
          } else {
            // Shell face is not used, only distinguish outer and inner face.
            marker = tsymface.tet != dummytet ? 1 : 0;
          }
        }
        if (b->neighout > 1) {
          // '-nn' switch. Output adjacent tets indices.
          neigh1 = * (int *)(tface.tet + elemmarkerindex);
          if (tsymface.tet != dummytet) {
            neigh2 = * (int *)(tsymface.tet + elemmarkerindex);
          } else {
            neigh2 = -1;  
          }
        }
        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 (bmark) {
            // 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 (bmark) {
            emlist[facenumber - in->firstnumber] = marker;
          }
          if (b->neighout > 1) {
            out->adjtetlist[(facenumber - in->firstnumber) * 2]     = neigh1;
            out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
          }
        }
        facenumber++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outhullfaces()    Output outer boundary faces to a .face file or a        //
//                   tetgenio structure.                                     //
//                                                                           //
// The normal of each face is arranged to point inside of the domain (use    //
// right-hand rule).  This routines will outputs convex hull faces if the    //
// mesh is a Delaunay tetrahedralization.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outhullfaces(tetgenio* out)
{
  FILE *outfile;
  char facefilename[FILENAMESIZE];
  int *elist;
  int index;
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  int firstindex, shift;
  int facenumber;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  index = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(1);
    }
    fprintf(outfile, "%ld  0\n", hullsize);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[hullsize * 3];
    if (out->trifacelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    out->numberoftrifaces = hullsize;
    elist = out->trifacelist;
    index = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  facenumber = firstindex; // in->firstnumber;
  // To loop over the set of hull faces, loop over all tetrahedra, and look
  //   at the four faces of each one. If there isn't another tetrahedron
  //   adjacent to this face, operate on the face.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if (tsymface.tet == dummytet) {
        torg = org(tface);
        tdest = dest(tface);
        tapex = apex(tface);
        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++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  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 exist 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;
  char facefilename[FILENAMESIZE];
  int *elist;
  int *emlist;
  int index, index1, index2;
  triface abuttingtet;
  face faceloop;
  point torg, tdest, tapex;
  int bmark, faceid, marker;
  int firstindex, shift;
  int neigh1, neigh2;
  int facenumber;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index = index1 = index2 = 0;
  faceid = marker = 0;
  neigh1 = neigh2 = 0;

  bmark = !b->nobound && in->facetmarkerlist;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(1);
    }
    // Number of subfaces.
    fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[subfaces->items * 3];
    if (out->trifacelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    if (bmark) {
      // Allocate memory for 'trifacemarkerlist'.
      out->trifacemarkerlist = new int[subfaces->items];
      if (out->trifacemarkerlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[subfaces->items * 2];
      if (out->adjtetlist == (int *) NULL) {
        printf("Error:  Out of memory.\n");
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = subfaces->items;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  facenumber = firstindex; // in->firstnumber;
  while (faceloop.sh != (shellface *) NULL) {
    stpivot(faceloop, abuttingtet);
    if (abuttingtet.tet == dummytet) {
      sesymself(faceloop);
      stpivot(faceloop, abuttingtet);
    }
    if (abuttingtet.tet != dummytet) {
      // 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.
      adjustedgering(abuttingtet, CCW);
      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);
    }
    if (bmark) {
      faceid = shellmark(faceloop) - 1;
      marker = in->facetmarkerlist[faceid];
    }
    if (b->neighout > 1) {
      // '-nn' switch. Output adjacent tets indices.
      neigh1 = -1;
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != dummytet) {
        neigh1 = * (int *)(abuttingtet.tet + elemmarkerindex);
      }
      neigh2 = -1;
      sesymself(faceloop);
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != dummytet) {
        neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex);
      }
    }
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift,
              pointmark(tapex) - shift);
      if (bmark) {
        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 (bmark) {
        emlist[index1++] = marker;
      }
      if (b->neighout > 1) {
        out->adjtetlist[index2++] = neigh1;
        out->adjtetlist[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 structure.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outedges(tetgenio* out)
{
  FILE *outfile;
  char edgefilename[FILENAMESIZE];
  int *elist, *emlist;
  int index, index1;
  triface tetloop, worktet, spintet;
  face checksh;
  point torg, tdest;
  long faces, edges;
  int firstindex, shift;
  int edgenumber, faceid, marker;
  int hitbdry, i;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index = index1 = 0;
  faceid = marker = 0;

  // Using the Euler formula (V-E+F-T=1) to get the total number of edges.
  faces = (4l * tetrahedrons->items + hullsize) / 2l;
  edges = points->items + faces - tetrahedrons->items - 1l;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(1);
    }
    // Write the number of edges, boundary markers (0 or 1).
    fprintf(outfile, "%ld  %d\n", edges, !b->nobound);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[edges * 2];
    if (out->edgelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    if (!b->nobound) {
      out->edgemarkerlist = new int[edges];
    }
    out->numberofedges = edges;
    elist = out->edgelist;
    emlist = out->edgemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift (reduce) the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  edgenumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        torg = org(worktet);
        tdest = dest(worktet);
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%5d   %4d  %4d", edgenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift);
        } else {
          // Output three vertices of this face;
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
        }
        if (!b->nobound) {
          if (hitbdry > 0) {
            // It is a boundary edge. Get the boundary marker of the facet
            //   containing this edge. Note there may have more than one
            //   facet, choose one arbitrarily.
            if ((b->plc || b->refine) && in->facetmarkerlist) {
              tspivot(spintet, checksh);
              faceid = shellmark(checksh) - 1;
              marker = in->facetmarkerlist[faceid];
            } else {
              marker = 1;  // Indicate it's a boundary edge.
            }
          } else {
            marker = 0;
          }
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, "  %d", marker);
          } else {
            emlist[index1++] = marker;
          }
        }
        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);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsubsegments()    Output segments to a .edge file or a structure.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsubsegments(tetgenio* out)
{
  FILE *outfile;
  char edgefilename[FILENAMESIZE];
  int *elist;
  int index;
  face edgeloop;
  point torg, tdest;
  int firstindex, shift;
  int edgenumber;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  index = 0;  

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(1);
    }
    // Number of subsegments.
    fprintf(outfile, "%ld\n", subsegs->items);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[subsegs->items * 2];
    if (out->edgelist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    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.
  }

  subsegs->traversalinit();
  edgeloop.sh = shellfacetraverse(subsegs);
  edgenumber = firstindex; // in->firstnumber;
  while (edgeloop.sh != (shellface *) NULL) {
    torg = sorg(edgeloop);
    tdest = sdest(edgeloop);
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%5d   %4d  %4d\n", edgenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift);
    } else {
      // Output three vertices of this face;
      elist[index++] = pointmark(torg) - shift;
      elist[index++] = pointmark(tdest) - shift;
    }
    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;
  char neighborfilename[FILENAMESIZE];
  int *nlist;
  int index;
  triface tetloop, tetsym;
  int neighbor1, neighbor2, neighbor3, neighbor4;
  int firstindex;
  int elementnumber;

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

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  nlist = (int *) NULL;
  index = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(neighborfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", neighborfilename);
      terminatetetgen(1);
    }
    // Number of tetrahedra, four faces per tetrahedron.
    fprintf(outfile, "%ld  %d\n", tetrahedrons->items, 4);
  } else {
    // Allocate memory for 'neighborlist'.
    out->neighborlist = new int[tetrahedrons->items * 4];
    if (out->neighborlist == (int *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
    nlist = out->neighborlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) NULL) {
    tetloop.loc = 2;
    sym(tetloop, tetsym);
    neighbor1 = * (int *) (tetsym.tet + elemmarkerindex);
    tetloop.loc = 3;
    sym(tetloop, tetsym);
    neighbor2 = * (int *) (tetsym.tet + elemmarkerindex);
    tetloop.loc = 1;
    sym(tetloop, tetsym);
    neighbor3 = * (int *) (tetsym.tet + elemmarkerindex);
    tetloop.loc = 0;
    sym(tetloop, tetsym);
    neighbor4 = * (int *) (tetsym.tet + elemmarkerindex);
    if (out == (tetgenio *) NULL) {
      // Tetrahedra number, neighboring tetrahedron numbers.
      fprintf(outfile, "%4d    %4d  %4d  %4d  %4d\n", elementnumber,
              neighbor1, neighbor2, neighbor3, neighbor4);
    } else {
      nlist[index++] = neighbor1;
      nlist[index++] = neighbor2;
      nlist[index++] = neighbor3;
      nlist[index++] = neighbor4;
    }
    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 interal Delaunay edge. At a //
// ridge, it is unbounded.  Each Voronoi cell is the convex hull of all Vor- //
// onoi vertices around a common Delaunay vertex. It is a polytope for any   //
// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay    //
// vertex belonging to the convex hull.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outvoronoi(tetgenio* out)
{
  FILE *outfile;
  char outfilename[FILENAMESIZE];
  tetgenio::voroedge *vedge;
  tetgenio::vorofacet *vfacet;
  list *tetlist, *ptlist;
  triface tetloop, worktet, spintet;
  point pt[4], ptloop, neipt;
  REAL ccent[3], infvec[3], vec1[3], vec2[3], L;
  long faces, edges;
  int *tetfaceindexarray, *tetedgeindexarray;
  int arraysize, *vertarray;
  int vpointcount, vedgecount, vfacecount, tcount;
  int index, shift;
  int end1, end2;
  int hitbdry, i, j, k;

  // 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);
  // The number of Delaunay faces (= the number of Voronoi edges).
  faces = (4l * tetrahedrons->items + hullsize) / 2l;
  // The number of Delaunay edges (= the number of Voronoi faces).
  edges = points->items + faces - tetrahedrons->items - 1;
  outfile = (FILE *) NULL; // Avoid compile warnings.

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(1);
    }
    // Number of voronoi points, 3 dim, no attributes, no marker.
    fprintf(outfile, "%ld  3  0  0\n", tetrahedrons->items);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofvpoints = (int) tetrahedrons->items;
    out->vpointlist = new REAL[out->numberofvpoints * 3];
    if (out->vpointlist == (REAL *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }

  // Loop the tetrahedronlist once, do the following: 
  //   (1) Output Voronoi vertices (the circumcenter of the tetrahedron).
  //   (2) Make a map from points-to-tetrahedra (for Voronoi cells).
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vpointcount = 0;
  index = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Calculate the circumcenter.
    for (i = 0; i < 4; i++) {
      pt[i] = (point) tetloop.tet[4 + i];
      setpoint2tet(pt[i], encode(tetloop));
    }
    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];
    }
    // Remember the index of this element.
    * (int *) (tetloop.tet + elemmarkerindex) = vpointcount;
    vpointcount++;
    tetloop.tet = tetrahedrontraverse();
  }
  // Set the outside element marker.
  * (int *) (dummytet + elemmarkerindex) = -1;

  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(1);
    }
    // Number of Voronoi edges, no marker.
    fprintf(outfile, "%ld  0\n", faces);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofedges = (int) faces;
    out->vedgelist = new tetgenio::voroedge[out->numberofvedges];
  }

  // Loop the tetrahedronlist once, output the Voronoi edges. The index of
  //   each Voronoi edge corresponding to the index of the Delaunay face.
  //   The four faces' indices of each tetrahedron are saved in the list
  //   'tetfaceindexarray', in the entry of i,  where i (0-based) is the
  //   index of this tetrahedron (= vpointcount). 
  tetfaceindexarray = new int[tetrahedrons->items * 4];  
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vedgecount = 0;
  index = 0;
  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 pointer is
    //   smaller than its neighbor's or the neighbor is outside.
    end1 = * (int *) (tetloop.tet + elemmarkerindex);
    for (i = 0; i < 4; i++) {
      decode(tetloop.tet[i], worktet);
      if ((worktet.tet == dummytet) || (tetloop.tet < worktet.tet)) {
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%4d  %4d", vedgecount + shift, end1 + shift);
        } else {
          vedge = &(out->vedgelist[index++]);
          vedge->v1 = end1 + shift;
        }
        end2 = * (int *) (worktet.tet + elemmarkerindex);
        // Note that end2 may be -1 (worktet.tet is outside).
        if (end2 == -1) {
          // Calculate the out normal of this hull face.
          worktet.tet = tetloop.tet;
          worktet.loc = i;
          worktet.ver = 1; // The CW edge ring.
          pt[0] = org(worktet);
          pt[1] = dest(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 face index in this tet and its neighbor if exists.
        tetfaceindexarray[end1 * 4 + i] = vedgecount;
        if (end2 != -1) {
          tetfaceindexarray[end2 * 4 + worktet.loc] = vedgecount;
        }
        vedgecount++;
      }
    }
    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(1);
    }
    // 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) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }

  // Loop the tetrahedronlist once, Output Voronoi facets. The index of each
  //   Voronoi facet corresponding to the index of the Delaunay edge.  The
  //   six edges' indices of each tetrahedron are saved in the list 'tetedge-
  //   indexarray', in the entry of i,  where i (0-based) is the index of
  //   this tetrahedron (= vpointcount). 
  tetedgeindexarray = new int[tetrahedrons->items * 6];
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vfacecount = 0;
  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 pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet = tetloop;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      // Now count the number of tets surrounding this edge.
      tcount = 1;
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
          tcount++;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        // Get the two endpoints of this edge.
        pt[0] = org(worktet);
        pt[1] = dest(worktet);
        end1 = pointmark(pt[0]) - in->firstnumber;
        end2 = pointmark(pt[1]) - in->firstnumber;
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%4d  %4d %4d  %-2d ", vfacecount + shift, 
                  end1 + shift, end2 + shift, tcount + (hitbdry > 0));
        } else {
          vfacet = &(out->vfacetlist[vfacecount]);
          vfacet->c1 = end1 + shift;
          vfacet->c2 = end2 + shift;
          vfacet->elist = new int[tcount + (hitbdry > 0) + 1];
          vfacet->elist[0] = tcount + (hitbdry > 0);
          index = 1;
        }
        // If hitbdry > 0, then spintet is a hull face.
        if (hitbdry > 0) {
          // The edge list starts with a ray.
          vpointcount = * (int *) (spintet.tet + elemmarkerindex);
          vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc];
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " %d", vedgecount + shift);
          } else {
            vfacet->elist[index++] = vedgecount + shift;
          }
          // Save this facet number in tet.
          tetedgeindexarray[vpointcount * 6 + 
            locver2edge[spintet.loc][spintet.ver]] = vfacecount;
          esymself(spintet);
          fnextself(spintet); // In the same tet.
        }
        // Output internal Voronoi edges.
        for (j = 0; j < tcount; j++) {
          vpointcount = * (int *) (spintet.tet + elemmarkerindex);
          vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc];
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " %d", vedgecount + shift);
          } else {
            vfacet->elist[index++] = vedgecount + shift;
          }
          // Save this facet number in tet.
          tetedgeindexarray[vpointcount * 6 + 
            locver2edge[spintet.loc][spintet.ver]] = vfacecount;
          fnextself(spintet);
        }
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "\n");
        }
        vfacecount++;
      }
    } // 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(1);
    }
    // Number of Voronoi cells.
    fprintf(outfile, "%ld\n", points->items);
  } else {
    out->numberofvcells = points->items;
    out->vcelllist = new int*[out->numberofvcells];
    if (out->vcelllist == (int **) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }

  // Loop through point list, for each point, output a Voronoi cell.
  tetlist = new list(sizeof(triface), NULL, 256);
  ptlist = new list(sizeof(point *), NULL, 256);
  points->traversalinit();
  ptloop = pointtraverse();
  vpointcount = 0;
  while (ptloop != (point) NULL) {
    decode(point2tet(ptloop), tetloop);
    // assert(!isdead(&tetloop));
    if (!isdead(&tetloop)) {
      // Form the star of p.
      tetlist->append(&tetloop);
      formstarpolyhedron(ptloop, tetlist, ptlist, true);
      tcount = ptlist->len();
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "%4d  %-2d ", vpointcount + shift, tcount);
      } else {
        arraysize = tcount;
        vertarray = out->vcelllist[vpointcount];
        vertarray = new int[arraysize + 1];
        vertarray[0] = arraysize;
        index = 1;
      }
      // List Voronoi facets bounding this cell.
      for (i = 0; i < ptlist->len(); i++) {
        neipt =  * (point *)(* ptlist)[i];
        // Find a tet in tetlist having edge (ptloop, neipt) -- Very Slow.
        for (j = 0; j < tetlist->len(); j++) {
          tetloop = * (triface *)(* tetlist)[j];
          for (k = 0; k < 6; k++) {
            tetloop.loc = edge2locver[k][0];
            tetloop.ver = edge2locver[k][1];
            if (org(tetloop) == ptloop) {
              if (dest(tetloop) == neipt) break;
            } else if (org(tetloop) == neipt) {
              if (dest(tetloop) == ptloop) break;
            }
          }
          if (k < 6) break; // Found this edge.
        }
        assert(j < tetlist->len());
        // k is the right edge number.        
        end1 = * (int *) (tetloop.tet + elemmarkerindex);
        vfacecount = tetedgeindexarray[end1 * 6 + k];
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, " %d", vfacecount + shift);
        } else {
          vertarray[index++] = vfacecount + shift;
        }
      } // for (i = 0; i < ptlist->len(); i++) {
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "\n");
      }
      vpointcount++;
    }
    tetlist->clear();
    ptlist->clear();
    ptloop = pointtraverse();
  }
  delete tetlist;
  delete ptlist;
  delete [] tetfaceindexarray;
  delete [] tetedgeindexarray;

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outpbcnodes()    Output pbc node pairs to a .pbc file or a structure.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outpbcnodes(tetgenio* out)
{ 
  FILE *outfile;
  char pbcfilename[FILENAMESIZE];
  list *ptpairlist;
  tetgenio::pbcgroup *pgi, *pgo;
  pbcdata *pd;
  face faceloop;
  face checkseg, symseg;
  point *ptpair, pa, pb;
  enum locateresult loc;
  REAL sympt[3], d1, d2;
  int *worklist;
  int firstindex, shift;
  int index, idx;
  int i, j, k, l;

  if (out == (tetgenio *) NULL) {
    strcpy(pbcfilename, b->outfilename);
    strcat(pbcfilename, ".pbc");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      printf("Writing %s.\n", pbcfilename);
    } else {
      printf("Writing pbc nodes.\n");
    }
  }

  // Avoid compilation warnings.
  outfile = (FILE *) NULL;
  pgo = (tetgenio::pbcgroup *) NULL;
  index = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(pbcfilename, "w");
    if (outfile == (FILE *) NULL) {
      printf("File I/O Error:  Cannot create file %s.\n", pbcfilename);
      terminatetetgen(1);
    }
    // Number of pbc groups.
    fprintf(outfile, "# number of PBCs.\n");
    fprintf(outfile, "%d\n\n", in->numberofpbcgroups);
  } else {
    out->numberofpbcgroups = in->numberofpbcgroups;
    // Allocate memory for 'out->pbcgrouplist'.
    out->pbcgrouplist = new tetgenio::pbcgroup[in->numberofpbcgroups];
    // (Next line was a bug, reported by Murry Nigel). 
    if (out->pbcgrouplist == (tetgenio::pbcgroup *) NULL) {
      printf("Error:  Out of memory.\n");
      terminatetetgen(1);
    }
  }

  ptpairlist = new list(2 * sizeof(point *), NULL, 256);
  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 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.
  }

  for (i = 0; i < in->numberofpbcgroups; i++) {
    // Group i.
    pgi = &(in->pbcgrouplist[i]);
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "# PBC %d\n", in->firstnumber + i);
      // Output facet markers.
      fprintf(outfile, "%d  %d\n", pgi->fmark1, pgi->fmark2);
      // Output transformation matrix.
      fprintf(outfile, "[\n");
      for (j = 0; j < 4; j++) {
        fprintf(outfile, "  %.12g %.12g %.12g %.12g\n", pgi->transmat[j][0],
                pgi->transmat[j][1], pgi->transmat[j][2], pgi->transmat[j][3]);
      }
      fprintf(outfile, "]\n");
    } else {
      pgo = &(out->pbcgrouplist[i]);
      // Copy data from pgi to pgo.
      pgo->fmark1 = pgi->fmark1;
      pgo->fmark2 = pgi->fmark2;
      for (j = 0; j < 4; j++) {
        for (k = 0; k < 4; k++) pgo->transmat[j][k] = pgi->transmat[j][k];
      }
    }

    // Find the point pairs of group i.
    subfaces->traversalinit();
    faceloop.sh = shellfacetraverse(subfaces);
    while (faceloop.sh != (shellface *) NULL) {
      if (shellpbcgroup(faceloop) == i) {
        // It is in group i. Operate on it if it has pgi->fmark1.
        idx = shellmark(faceloop) - 1;
        if (in->facetmarkerlist[idx] == pgi->fmark1) {
          // Loop three edges of the subface.
          for (j = 0; j < 3; j++) {
            sspivot(faceloop, checkseg);
            // Loop two vertices of the edge.
            for (k = 0; k < 2; k++) {
              if (k == 0) pa = sorg(faceloop);
              else pa = sdest(faceloop);
              if (worklist[pointmark(pa)] == 0) {
                pb = (point) NULL;
                if (checkseg.sh != dummysh) {
                  // pa is on a segment. Find pb.
                  // Find the incident pbcgroup of checkseg.
                  idx = shellmark(checkseg) - 1;
                  for (l = idx2segpglist[idx]; l < idx2segpglist[idx + 1];
                       l++) {
                    pd = (pbcdata *)(* segpbcgrouptable)[segpglist[l]];
                    if (((pd->fmark[0] == pgi->fmark1) &&
                         (pd->fmark[1] == pgi->fmark2)) ||
                        ((pd->fmark[0] == pgi->fmark2) &&
                         (pd->fmark[1] == pgi->fmark1))) break;
                  }
#ifdef SELF_CHECK
                  assert(l < idx2segpglist[idx + 1]);
#endif
                  loc = getsegpbcsympoint(pa, &checkseg, sympt, &symseg,
                                          segpglist[l]);
                  if (loc != ONVERTEX) {
                    // Not found a match point! It may be caused by the
                    //   pair of input vertices don't have enough digits.
                    //   Choose a near vertex.
                    d1 = distance(sympt, sorg(symseg));
                    d2 = distance(sympt, sdest(symseg));
                    if (d1 > d2) sesymself(symseg);
                  }
                  pb = sorg(symseg);
                } else {
                  // Operate on pa if it is inside the facet.
                  if (pointtype(pa) == FREESUBVERTEX) {
                    pb = point2pbcpt(pa);
                  }
                }
                if (pb != (point) NULL) {
                  // Add the pair (pa, pb) into list. 
                  ptpair = (point *) ptpairlist->append(NULL);
                  ptpair[0] = pa;
                  ptpair[1] = pb;
                  // Mark pa (avoid to operate on it later).
                  worklist[pointmark(pa)] = 1;
                }
              }
            }
            // Get the next edge.
            senextself(faceloop);
          }
        }
      }
      faceloop.sh = shellfacetraverse(subfaces);
    }

    // Output the list of pbc points.
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%d\n", ptpairlist->len());
    } else {
      pgo->numberofpointpairs = ptpairlist->len();
      pgo->pointpairlist = new int[pgo->numberofpointpairs * 2];
      index = 0;
    }
    for (j = 0; j < ptpairlist->len(); j++) {
      ptpair = (point *)(* ptpairlist)[j];
      pa = ptpair[0];
      pb = ptpair[1];
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "  %4d %4d\n", pointmark(pa) - shift,
                pointmark(pb) - shift);
      } else {
        pgo->pointpairlist[index++] = pointmark(pa) - shift;
        pgo->pointpairlist[index++] = pointmark(pb) - shift;
      }
      // Unmark pa.
      worklist[pointmark(pa)] = 0;
    }
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "\n");
    }
    ptpairlist->clear();
  }

  delete [] worklist;
  delete ptpairlist;

  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 faceid, 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;  

  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) {
      faceid = shellmark(faceloop) - 1;
      if (faceid >= 0) { 
        marker = in->facetmarkerlist[faceid];
      } else {
        marker = 0; // This subface must be added manually later.
      }
    }
    fprintf(outfile, "3    %4d  %4d  %4d", pointmark(p1) - shift,
            pointmark(p2) - shift, pointmark(p3) - shift);
    if (bmark) {
      fprintf(outfile, "    %d", marker);
    }
    fprintf(outfile, "\n");
    faceloop.sh = shellfacetraverse(subfaces);
  }

  // Copy input holelist.
  fprintf(outfile, "\n# part 3: hole list.\n");
  fprintf(outfile, "%d\n", in->numberofholes);
  for (i = 0; i < in->numberofholes; i++) {
    fprintf(outfile, "%d  %g  %g  %g\n", i + in->firstnumber,
            in->holelist[i * 3], in->holelist[i * 3 + 1],
            in->holelist[i * 3 + 2]);
  }

  // Copy input regionlist.
  fprintf(outfile, "\n# part 4: region list.\n");
  fprintf(outfile, "%d\n", in->numberofregions);
  for (i = 0; i < in->numberofregions; i++) {
    fprintf(outfile, "%d  %g  %g  %g  %d  %g\n", i + in->firstnumber,
            in->regionlist[i * 5], in->regionlist[i * 5 + 1],
            in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3],
            in->regionlist[i * 5 + 4]);
  }

  fprintf(outfile, "# Generated by %s\n", b->commandline);
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2medit()    Write mesh to a .mesh file, which can be read and      //
//                    rendered by Medit (a free mesh viewer from INRIA).     //
//                                                                           //
// You can specify a filename (without suffix) in 'mfilename'.  If you don't //
// supply a filename (let mfilename be 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 segloop, checkmark;
  point ptloop, p1, p2, p3, p4;
  long faces;
  int pointnumber;
  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");

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

  // Compute the number of edges.
  faces = (4l * tetrahedrons->items + hullsize) / 2l;

  fprintf(outfile, "\n# Set of Triangles\n");
  fprintf(outfile, "Triangles\n");
  fprintf(outfile, "%ld\n", faces);

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each tetrahedron. If there isn't another tetrahedron
  //   adjacent to the face, operate on the face.  If there is another adj-
  //   acent tetrahedron, operate on the face only if the current tetrahedron
  //   has a smaller pointer than its neighbor.  This way, each face is
  //   considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if (tface.tet < tsymface.tet || tsymface.tet == dummytet) {
        p1 = org (tface);
        p2 = dest(tface);
        p3 = apex(tface);
        fprintf(outfile, "%5d  %5d  %5d",
                pointmark(p1), pointmark(p2), pointmark(p3));
        fprintf(outfile, "    0\n");
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "\n# Set of Tetrahedra\n");
  fprintf(outfile, "Tetrahedra\n");
  fprintf(outfile, "%ld\n", tetrahedrons->items);

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  while (tetptr != (tetrahedron *) NULL) {
    p1 = (point) tetptr[4];
    p2 = (point) tetptr[5];
    p3 = (point) tetptr[6];
    p4 = (point) tetptr[7];
    fprintf(outfile, "%5d  %5d  %5d  %5d",
            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
    if (in->numberoftetrahedronattributes > 0) {
      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
    } else {
      fprintf(outfile, "  0");
    }
    fprintf(outfile, "\n");
    tetptr = tetrahedrontraverse();
  }

  fprintf(outfile, "\nCorners\n");
  fprintf(outfile, "%d\n", in->numberofpoints);

  for (i = 0; i < in->numberofpoints; i++) {
    fprintf(outfile, "%4d\n", i + 1);
  }

  if (b->useshelles) {
    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));
      fprintf(outfile, "    0\n");
      segloop.sh = shellfacetraverse(subsegs);
    }
  }

  fprintf(outfile, "\nEnd\n");
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2gid()    Write mesh to a .ele.msh file and a .face.msh file,      //
//                  which can be imported and rendered by Gid.               //
//                                                                           //
// You can specify a filename (without suffix) in 'gfilename'.  If you don't //
// supply a filename (let gfilename be NULL), the default name stored in     //
// 'tetgenbehavior' will be used. The suffixes (.ele.msh and .face.msh) will //
// be automatically added.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2gid(char* gfilename)
{
  FILE *outfile;
  char gidfilename[FILENAMESIZE];
  tetrahedron* tetptr;
  triface tface, tsymface;
  face sface;
  point ptloop, p1, p2, p3, p4;
  int pointnumber;
  int elementnumber;

  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
    strcpy(gidfilename, gfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(gidfilename, b->outfilename);
  } else {
    strcpy(gidfilename, "unnamed");
  }
  strcat(gidfilename, ".ele.msh");

  if (!b->quiet) {
    printf("Writing %s.\n", gidfilename);
  }
  outfile = fopen(gidfilename, "w");
  if (outfile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
    return;
  }

  fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n");
  fprintf(outfile, "coordinates\n");

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Gid need start number form 1.
  while (ptloop != (point) NULL) {
    // Point coordinates.
    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
            ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g", ptloop[3]);
    }
    fprintf(outfile, "\n");
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  fprintf(outfile, "end coordinates\n");
  fprintf(outfile, "elements\n");

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  elementnumber = 1;
  while (tetptr != (tetrahedron *) NULL) {
    p1 = (point) tetptr[4];
    p2 = (point) tetptr[5];
    p3 = (point) tetptr[6];
    p4 = (point) tetptr[7];
    fprintf(outfile, "%5d  %5d %5d %5d %5d", elementnumber,
            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
    if (in->numberoftetrahedronattributes > 0) {
      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
    } 
    fprintf(outfile, "\n");
    tetptr = tetrahedrontraverse();
    elementnumber++;
  }

  fprintf(outfile, "end elements\n");
  fclose(outfile);

  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
    strcpy(gidfilename, gfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(gidfilename, b->outfilename);
  } else {
    strcpy(gidfilename, "unnamed");
  }
  strcat(gidfilename, ".face.msh");

  if (!b->quiet) {
    printf("Writing %s.\n", gidfilename);
  }
  outfile = fopen(gidfilename, "w");
  if (outfile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
    return;
  }

  fprintf(outfile, "mesh dimension = 3 elemtype triangle nnode = 3\n");
  fprintf(outfile, "coordinates\n");

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Gid need start number form 1.
  while (ptloop != (point) NULL) {
    // Point coordinates.
    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
            ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g", ptloop[3]);
    }
    fprintf(outfile, "\n");
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  fprintf(outfile, "end coordinates\n");
  fprintf(outfile, "elements\n");

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  elementnumber = 1;
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
        p1 = org(tface);
        p2 = dest(tface);
        p3 = apex(tface);
        if (tsymface.tet == dummytet) {
          // It's a hull face, output it.
          fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
                  pointmark(p1), pointmark(p2), pointmark(p3));
          elementnumber++;
        } else if (b->useshelles) {
          // Only output it if it's a subface.
          tspivot(tface, sface);
          if (sface.sh != dummysh) {
            fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
                    pointmark(p1), pointmark(p2), pointmark(p3));
            elementnumber++;
          }
        }
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "end elements\n");
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2off()    Write the mesh to an .off file.                          //
//                                                                           //
// .off, the Object File Format, is one of the popular file formats from the //
// Geometry Center's Geomview package (http://www.geomview.org).             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2off(char* ofilename)
{
  FILE *outfile;
  char offfilename[FILENAMESIZE];
  triface tface, tsymface;
  point ptloop, p1, p2, p3;
  long faces;
  int shift;

  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
    strcpy(offfilename, ofilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(offfilename, b->outfilename);
  } else {
    strcpy(offfilename, "unnamed");
  }
  strcat(offfilename, ".off");

  if (!b->quiet) {
    printf("Writing %s.\n", offfilename);
  }
  outfile = fopen(offfilename, "w");
  if (outfile == (FILE *) NULL) {
    printf("File I/O Error:  Cannot create file %s.\n", offfilename);
    return;
  }

  // Calculate the number of triangular faces in the tetrahedral mesh.
  faces = (4l * tetrahedrons->items + hullsize) / 2l;

  // Number of points, faces, and edges(not used, here show hullsize).
  fprintf(outfile, "OFF\n%ld  %ld  %ld\n", points->items, faces, hullsize);

  // Write the points.
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    fprintf(outfile, " %.17g  %.17g  %.17g\n",ptloop[0], ptloop[1], ptloop[2]);
    ptloop = pointtraverse();
  }

  // OFF always use zero as the first index.
  shift = in->firstnumber == 1 ? 1 : 0;

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each tetrahedron. If there isn't another tetrahedron
  //   adjacent to the face, operate on the face.  If there is another adj-
  //   acent tetrahedron, operate on the face only if the current tetrahedron
  //   has a smaller pointer than its neighbor.  This way, each face is
  //   considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
        p1 = org(tface);
        p2 = dest(tface);
        p3 = apex(tface);
        // Face number, indices of three vertexs.
        fprintf(outfile, "3   %4d  %4d  %4d\n", pointmark(p1) - shift,
                pointmark(p2) - shift, pointmark(p3) - shift);
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "# Generated by %s\n", b->commandline);
  fclose(outfile);
}

//
// End of I/O rouitnes
//

//
// Begin of user interaction routines
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// internalerror()    Ask the user to send me the defective product.  Exit.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::internalerror()
{
  printf("  Please report this bug to sihang@mail.berlios.de. Include the\n");
  printf("    message above, your input data set, and the exact command\n");
  printf("    line you used to run this program, thank you.\n");
  terminatetetgen(2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkmesh()    Test the mesh for topological consistency.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::checkmesh()
{
  triface tetraloop;
  triface oppotet, oppooppotet;
  point tetorg, tetdest, tetapex, tetoppo;
  REAL oritest;
  int horrors;

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

  horrors = 0;
  // Run through the list of tetrahedra, checking each one.
  tetrahedrons->traversalinit();
  tetraloop.tet = tetrahedrontraverse();
  while (tetraloop.tet != (tetrahedron *) NULL) {
    // Check all four faces of the tetrahedron.
    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
      tetorg = org(tetraloop);
      tetdest = dest(tetraloop);
      tetapex = apex(tetraloop);
      tetoppo = oppo(tetraloop);
      if (tetraloop.loc == 0) {             // Only test for inversion once.
        oritest = orient3d(tetorg, tetdest, tetapex, tetoppo);
        if (oritest >= 0.0) {
          printf("  !! !! %s ", oritest > 0.0 ? "Inverted" : "Degenerated");
          printtet(&tetraloop);
          printf("  orient3d = %.17g.\n", oritest);
          horrors++;
        }
      }
      // Find the neighboring tetrahedron on this face.
      sym(tetraloop, oppotet);
      if (oppotet.tet != dummytet) {
        // Check that the tetrahedron's neighbor knows it's a neighbor.
        sym(oppotet, oppooppotet);
        if ((tetraloop.tet != oppooppotet.tet)
            || (tetraloop.loc != oppooppotet.loc)) {
          printf("  !! !! Asymmetric tetra-tetra bond:\n");
          if (tetraloop.tet == oppooppotet.tet) {
            printf("   (Right tetrahedron, wrong orientation)\n");
          }
          printf("    First ");
          printtet(&tetraloop);
          printf("    Second (nonreciprocating) ");
          printtet(&oppotet);
          horrors++;
        }
      }
    }
    tetraloop.tet = tetrahedrontraverse();
  }
  if (horrors == 0) {
    if (!b->quiet) {
      printf("  In my studied opinion, the mesh appears to be consistent.\n");
    }
  } else if (horrors == 1) {
    printf("  !! !! !! !! Precisely one festering wound discovered.\n");
  } else {
    printf("  !! !! !! !! %d abominations witnessed.\n", horrors);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkshells()       Test the boundary mesh for topological consistency.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::checkshells()
{
  triface oppotet, oppooppotet, testtet; 
  face shloop, segloop, spin;
  face testsh, testseg, testshsh;
  point shorg, shdest, segorg, segdest;
  REAL checksign;
  bool same;
  int horrors;
  int i;

  if (!b->quiet) {
    printf("  Checking consistency of the mesh boundary...\n");
  }
  horrors = 0;

  // Run through the list of subfaces, checking each one.
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != (shellface *) NULL) {
    // Check two connected tetrahedra if they exist.
    shloop.shver = 0;
    stpivot(shloop, oppotet);
    if (oppotet.tet != dummytet) {
      tspivot(oppotet, testsh);
      if (testsh.sh != shloop.sh) {
        printf("  !! !! Wrong tetra-subface connection.\n");
        printf("    Tetra: ");
        printtet(&oppotet);
        printf("    Subface: ");
        printsh(&shloop);
        horrors++;
      }
      if (oppo(oppotet) != (point) NULL) {
        adjustedgering(oppotet, CCW);
        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
                             oppo(oppotet));
        if (checksign >= 0.0) {
          printf("  !! !! Wrong subface orientation.\n");
          printf("    Subface: ");
          printsh(&shloop);
          horrors++;
        }
      }
    }
    sesymself(shloop);
    stpivot(shloop, oppooppotet);
    if (oppooppotet.tet != dummytet) {
      tspivot(oppooppotet, testsh);
      if (testsh.sh != shloop.sh) {
        printf("  !! !! Wrong tetra-subface connection.\n");
        printf("    Tetra: ");
        printtet(&oppooppotet);
        printf("    Subface: ");
        printsh(&shloop);
        horrors++;
      }
      if (oppotet.tet != dummytet) {
        sym(oppotet, testtet);
        if (testtet.tet != oppooppotet.tet) {
          printf("  !! !! Wrong tetra-subface-tetra connection.\n");
          printf("    Tetra 1: ");
          printtet(&oppotet);
          printf("    Subface: ");
          printsh(&shloop);
          printf("    Tetra 2: ");
          printtet(&oppooppotet);
          horrors++;
        }
      }
      if (oppo(oppooppotet) != (point) NULL) {
        adjustedgering(oppooppotet, CCW);
        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
                             oppo(oppooppotet));
        if (checksign >= 0.0) {
          printf("  !! !! Wrong subface orientation.\n");
          printf("    Subface: ");
          printsh(&shloop);
          horrors++;
        }
      }
    }
    // Check connection between subfaces.
    shloop.shver = 0;
    for (i = 0; i < 3; i++) {
      shorg = sorg(shloop);
      shdest = sdest(shloop);
      sspivot(shloop, testseg);
      if (testseg.sh != dummysh) {
        segorg = sorg(testseg);
        segdest = sdest(testseg);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          printf("  !! !! Wrong subface-subsegment connection.\n");
          printf("    Subface: ");
          printsh(&shloop);
          printf("    Subsegment: ");
          printsh(&testseg);
          horrors++;
        } 
      } 
      spivot(shloop, testsh);
      if (testsh.sh != dummysh) {
        segorg = sorg(testsh);
        segdest = sdest(testsh);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          printf("  !! !! Wrong subface-subface connection.\n");
          printf("    Subface 1: ");
          printsh(&shloop);
          printf("    Subface 2: ");
          printsh(&testsh);
          horrors++;
        }
        spivot(testsh, testshsh);
        shorg = sorg(testshsh);
        shdest = sdest(testshsh);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          printf("  !! !! Wrong subface-subface connection.\n");
          printf("    Subface 1: ");
          printsh(&testsh);
          printf("    Subface 2: ");
          printsh(&testshsh);
          horrors++;
        }
        if (testseg.sh == dummysh) {
          if (testshsh.sh != shloop.sh) {
            printf("  !! !! Wrong subface-subface connection.\n");
            printf("    Subface 1: ");
            printsh(&shloop);
            printf("    Subface 2: ");
            printsh(&testsh);
            horrors++;
          }
        } 
      }
      senextself(shloop);
    }
    shloop.sh = shellfacetraverse(subfaces);
  }

  // Run through the list of subsegs, checking each one.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    segorg = sorg(segloop);
    segdest = sdest(segloop);
    spivot(segloop, testsh);
    if (testsh.sh == dummysh) {
      printf("  !! !! Wrong subsegment-subface connection.\n");
      printf("    Subsegment: ");
      printsh(&segloop);
      horrors++;
      segloop.sh = shellfacetraverse(subsegs);
      continue;
    }
    shorg = sorg(testsh);
    shdest = sdest(testsh);
    same = ((shorg == segorg) && (shdest == segdest)) 
        || ((shorg == segdest) && (shdest == segorg));
    if (!same) {
      printf("  !! !! Wrong subsegment-subface connection.\n");
      printf("    Subsegment : ");
      printsh(&segloop);
      printf("    Subface : ");
      printsh(&testsh);
      horrors++;
      segloop.sh = shellfacetraverse(subsegs);
      continue;
    }
    // Check the connection of face loop around this subsegment.
    spin = testsh;
    i = 0;
    do {
      spivotself(spin);
      shorg = sorg(spin);
      shdest = sdest(spin);
      same = ((shorg == segorg) && (shdest == segdest)) 
          || ((shorg == segdest) && (shdest == segorg));
      if (!same) {
        printf("  !! !! Wrong subsegment-subface connection.\n");
        printf("    Subsegment : ");
        printsh(&segloop);
        printf("    Subface : ");
        printsh(&testsh);
        horrors++;
        break;
      }
      i++;
    } while (spin.sh != testsh.sh && i < 1000);
    if (i >= 1000) {
      printf("  !! !! Wrong subsegment-subface connection.\n");
      printf("    Subsegment : ");
      printsh(&segloop);
      horrors++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }
  if (horrors == 0) {
    if (!b->quiet) {
      printf("  Mesh boundaries connected correctly.\n");
    }
  } else {
    printf("  !! !! !! !! %d boundary connection viewed with horror.\n",
           horrors);
    return;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkdelaunay()    Ensure that the mesh is constrained Delaunay.          //
//                                                                           //
// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue)
{
  triface tetraloop;
  triface oppotet;
  face opposhelle;
  point tetorg, tetdest, tetapex, tetoppo;
  point oppooppo;
  enum fliptype fc;
  REAL sign;
  int shouldbedelaunay;
  int horrors;

  if (!b->quiet) {
    printf("  Checking Delaunay property of the mesh...\n");
  }
  horrors = 0;
  // Run through the list of triangles, checking each one.
  tetrahedrons->traversalinit();
  tetraloop.tet = tetrahedrontraverse();
  while (tetraloop.tet != (tetrahedron *) NULL) {
    // Check all four faces of the tetrahedron.
    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
      tetorg = org(tetraloop);
      tetdest = dest(tetraloop);
      tetapex = apex(tetraloop);
      tetoppo = oppo(tetraloop);
      sym(tetraloop, oppotet);
      oppooppo = oppo(oppotet);
      // Only do testif there is an adjoining tetrahedron whose pointer is
      //   larger (to ensure that each pair isn't tested twice).
      shouldbedelaunay = (oppotet.tet != dummytet)
                          && (tetoppo != (point) NULL)
                          && (oppooppo != (point) NULL)
                          && (tetraloop.tet < oppotet.tet);
      if (checksubfaces && shouldbedelaunay) {
        // If a shell face separates the tetrahedra, then the face is
        //   constrained, so no local Delaunay test should be done.
        tspivot(tetraloop, opposhelle);
        if (opposhelle.sh != dummysh){
          shouldbedelaunay = 0;
        }
      }
      if (shouldbedelaunay) {
        sign = insphere(tetdest, tetorg, tetapex, tetoppo, oppooppo);
        if ((sign > 0.0) && (eps > 0.0)) {
          if (iscospheric(tetdest, tetorg, tetapex, tetoppo, oppooppo, sign,
                          eps)) sign = 0.0;
        }
        if (sign > 0.0) {
          if (flipqueue) {
            enqueueflipface(tetraloop, flipqueue);
          } else {
            printf("  !! Non-locally Delaunay face (%d, %d, %d) ",
                   pointmark(tetorg), pointmark(tetdest), pointmark(tetapex));
            fc = categorizeface(tetraloop);
            switch (fc) {
            case T23: printf("\"T23\""); break;
            case T32: printf("\"T32\""); break;
            case T22: printf("\"T22\""); break;
            case T44: printf("\"T44\""); break;
            case N32: printf("\"N32\""); break;
            case N40: printf("\"N40\""); break;
            case FORBIDDENFACE:printf("\"FORBIDDENFACE\""); break;
            case FORBIDDENEDGE:printf("\"FORBIDDENEDGE\""); break;
            }
            printf("\n");
          }
          horrors++;
        }
      }
    }
    tetraloop.tet = tetrahedrontraverse();
  }
  if (flipqueue == (queue *) NULL) {
    if (horrors == 0) {
      if (!b->quiet) {
        printf("  The mesh is %s.\n",
               checksubfaces ? "constrained Delaunay" : "Delaunay");
      }
    } else {
      printf("  !! !! !! !! %d obscenities viewed with horror.\n", horrors);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkconforming()    Ensure that the mesh is conforming Delaunay.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::checkconforming()
{
  face segloop, shloop;
  int encsubsegs, encsubfaces;

  if (!b->quiet) {
    printf("  Checking conforming Delaunay property of mesh...\n");
  }
  encsubsegs = encsubfaces = 0;
  // Run through the list of subsegments, check each one.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    if (checkseg4encroach(&segloop, NULL, NULL, false)) {
      printf("  !! !! Non-conforming subsegment: (%d, %d)\n",
             pointmark(sorg(segloop)), pointmark(sdest(segloop)));
      encsubsegs++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }
  // Run through the list of subfaces, check each one.
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != (shellface *) NULL) {
    if (checksub4encroach(&shloop, NULL, false)) {
      printf("  !! !! Non-conforming subface: (%d, %d, %d)\n",
             pointmark(sorg(shloop)), pointmark(sdest(shloop)),
             pointmark(sapex(shloop)));
      encsubfaces++;
    }
    shloop.sh = shellfacetraverse(subfaces);
  }
  if (encsubsegs == 0 && encsubfaces == 0) {
    if (!b->quiet) {
      printf("  The mesh is conforming Delaunay.\n");
    }
  } else {
    if (encsubsegs > 0) {
      printf("  !! !! %d subsegments are non-conforming.\n", encsubsegs);
    }
    if (encsubfaces > 0) {
      printf("  !! !! %d subfaces are non-conforming.\n", encsubfaces);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// algorithmicstatistics()    Print statistics about the mesh algorithms.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifdef SELF_CHECK

void tetgenmesh::algorithmicstatistics()
{
  /*
  printf("Algorithmic statistics:\n\n");
  printf("  Point location millisecond:  %g\n", (REAL) tloctime * 1e+3);
  printf("  Flip millisecond:  %g\n", (REAL) tfliptime * 1e+3);
  if (b->plc || b->refine) {
    printf("  Number of facet above points calculations: %ld\n", abovecount);
  }
  if (b->plc) {
    printf("  Segment split rules: R1 %ld, R2 %ld, R3 %ld\n", r1count, r2count,
           r3count);
  }
  if (b->quality) {
    printf("  Bowyer-Watson insertions: seg %ld, sub %ld, vol %ld.\n",
           bowatsegcount, bowatsubcount, bowatvolcount);
    printf("  Bowyer-Watson corrections: seg %ld, sub %ld, vol %ld\n",
           updsegcount, updsubcount, updvolcount);
    printf("  Bowyer-Watson failures: seg %ld, sub %ld, vol %ld\n",
           failsegcount, failsubcount, failvolcount);
    printf("  Number of repair flips: %ld.\n", repairflipcount);
    printf("  Number of circumcenters outside Bowat-cav.: %ld.\n",
           outbowatcircumcount);
    if (b->conformdel) {
      printf("  Segment split rules: R2 %ld, R3 %ld\n", r2count, r3count);
      printf("  Number of CDT enforcement points: %ld.\n", cdtenforcesegpts);
    }
    printf("  Number of Rejections: seg %ld, sub %ld, tet %ld.\n", rejsegpts,
           rejsubpts, rejtetpts);
    if (b->optlevel) {
      printf(
      "  Optimization flips: f32 %ld, f44 %ld, f56 %ld, f68 %ld, fnm %ld.\n",
             optcount[3], optcount[4], optcount[5], optcount[6], optcount[9]);
      printf("  Optimization segment deletions: %ld.\n", optcount[1]);
    }
  }
  printf("\n");
  */
}

#endif // #ifdef SELF_CHECK

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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 smallestdiangle, biggestdiangle;
  REAL smallestfaangle, biggestfaangle;
  REAL tetvol, minaltitude;
  REAL cirradius, minheightinv; // insradius;
  REAL shortlen, longlen;
  REAL tetaspect, tetradius;
  REAL smalldiangle, bigdiangle;
  REAL smallfaangle, bigfaangle;
  int radiustable[12];
  int aspecttable[16];
  int dihedangletable[18];
  int faceangletable[18];
  int indx[4];
  int radiusindex;
  int aspectindex;
  int tendegree;
  int i, j;

  printf("Mesh quality statistics:\n\n");

  // Avoid compile warnings.
  shortlen = longlen = 0.0;
  smalldiangle = bigdiangle = 0.0;

  radiusratiotable[0]  =    0.707;    radiusratiotable[1]  =     1.0;
  radiusratiotable[2]  =      1.1;    radiusratiotable[3]  =     1.2;
  radiusratiotable[4]  =      1.4;    radiusratiotable[5]  =     1.6;
  radiusratiotable[6]  =      1.8;    radiusratiotable[7]  =     2.0;
  radiusratiotable[8]  =      2.5;    radiusratiotable[9]  =     3.0;
  radiusratiotable[10] =     10.0;    radiusratiotable[11] =     0.0;

  aspectratiotable[0]  =      1.5;    aspectratiotable[1]  =     2.0;
  aspectratiotable[2]  =      2.5;    aspectratiotable[3]  =     3.0;
  aspectratiotable[4]  =      4.0;    aspectratiotable[5]  =     6.0;
  aspectratiotable[6]  =     10.0;    aspectratiotable[7]  =    15.0;
  aspectratiotable[8]  =     25.0;    aspectratiotable[9]  =    50.0;
  aspectratiotable[10] =    100.0;    aspectratiotable[11] =     0.0;
  
  for (i = 0; i < 12; i++) radiustable[i] = 0;
  for (i = 0; i < 12; i++) aspecttable[i] = 0;
  for (i = 0; i < 18; i++) dihedangletable[i] = 0;
  for (i = 0; i < 18; i++) faceangletable[i] = 0;

  minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
  minaltitude = minaltitude * minaltitude;
  shortest = minaltitude;
  longest = 0.0;
  smallestvolume = minaltitude;
  biggestvolume = 0.0;
  smallestdiangle = smallestfaangle = 180.0;
  biggestdiangle = biggestfaangle = 0.0;

  // Loop all elements, calculate quality parameters for each element.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    
    // Get four vertices: p0, p1, p2, p3.
    for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i];
    // 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.
    // 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.
    lu_decmp(A, 3, indx, &D, 0);   
    // Get the tet volume.
    tetvol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
    // Get the three faces normals.
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) rhs[i] = 0.0;
      rhs[j] = 1.0;  // Positive means the inside direction
      lu_solve(A, 3, indx, rhs, 0);
      for (i = 0; i < 3; i++) N[j][i] = rhs[i];
    }
    // Get the fourth face normal by summing up the first three.
    for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
    // Get the radius of the circumsphere.
    for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]);
    lu_solve(A, 3, indx, rhs, 0);
    cirradius = sqrt(dot(rhs, rhs));
    // Normalize the face normals.
    for (i = 0; i < 4; i++) {
      // H[i] is the inverse of height of its corresponding face.
      H[i] = sqrt(dot(N[i], N[i]));
      for (j = 0; j < 3; j++) N[i][j] /= H[i];
    }
    // Get the radius of the inscribed sphere.
    // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
    // Get the biggest H[i] (corresponding to the smallest height).
    minheightinv = H[0];
    for (i = 1; i < 3; i++) {
      if (H[i] > minheightinv) minheightinv = H[i];
    }
    // Get the squares of the edge lengthes.
    for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]);
    // 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 longest and shortest edge length.
    for (i = 0; i < 6; i++) {
      if (i == 0) {
        shortlen = longlen = edgelength[i];
      } else {
        shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
        longlen  = edgelength[i] > longlen  ? edgelength[i] : longlen;
      }
      if (edgelength[i] > longest) {
        longest = edgelength[i];
      } 
      if (edgelength[i] < shortest) {
        shortest = edgelength[i];
      }
    }
    
    // Calculate the largest and smallest volume.
    if (tetvol < smallestvolume) {
      smallestvolume = tetvol;
    } 
    if (tetvol > biggestvolume) {
      biggestvolume = tetvol;
    }
    
    // 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 (smalldiangle < 5.0) {
      tendegree = 0;
    } else if (smalldiangle >= 5.0 && smalldiangle < 10.0) {
      tendegree = 1;
    } else if (smalldiangle >= 80.0 && smalldiangle < 110.0) {
      tendegree = 9; // Angles between 80 to 110 degree are in one entry.
    } else {
      tendegree = (int) (smalldiangle / 10.);
      if (smalldiangle < 80.0) {
        tendegree++;  // In the left column.
      } else {
        tendegree--;  // In the right column.
      }
    }
    dihedangletable[tendegree]++;
    if (bigdiangle >= 80.0 && bigdiangle < 110.0) {
      tendegree = 9; // Angles between 80 to 110 degree are in one entry.
    } else if (bigdiangle >= 170.0 && bigdiangle < 175.0) {
      tendegree = 16;
    } else if (bigdiangle >= 175.0) {
      tendegree = 17;
    } else {
      tendegree = (int) (bigdiangle / 10.);
      if (bigdiangle < 80.0) {
        tendegree++;  // In the left column.
      } else {
        tendegree--;  // In the right column.
      }
    }
    dihedangletable[tendegree]++;

    // Calulate the largest and smallest face angles.
    tetloop.ver = 0;
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, neightet);
      // Only do the calulation once for a face.
      if ((neightet.tet == dummytet) || (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) (smallfaangle / 10.);
        faceangletable[tendegree]++;
        tendegree = (int) (bigfaangle / 10.);
        faceangletable[tendegree]++;
      }
    }

    // Calculate aspect ratio and radius-edge ratio for this element.
    tetradius = cirradius / sqrt(shortlen);
    // tetaspect = sqrt(longlen) / (2.0 * insradius);
    tetaspect = sqrt(longlen) * minheightinv;
    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);
  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("  Radius-edge ratio histogram:\n");
  printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
         radiusratiotable[0], radiustable[0], radiusratiotable[5],
         radiusratiotable[6], radiustable[6]);
  for (i = 1; i < 5; i++) {
    printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
           radiusratiotable[i - 1], radiusratiotable[i], radiustable[i],
           radiusratiotable[i + 5], radiusratiotable[i + 6],
           radiustable[i + 6]);
  }
  printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
         radiusratiotable[4], radiusratiotable[5], radiustable[5],
         radiusratiotable[10], radiustable[11]);
  printf("  (A tetrahedron's radius-edge ratio is its radius of ");
  printf("circumsphere divided\n");
  printf("    by its shortest edge length)\n\n");
  */

  printf("  Aspect ratio histogram:\n");
  printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
         aspectratiotable[0], aspecttable[0], aspectratiotable[5],
         aspectratiotable[6], aspecttable[6]);
  for (i = 1; i < 5; i++) {
    printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
           aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
           aspectratiotable[i + 5], aspectratiotable[i + 6],
           aspecttable[i + 6]);
  }
  printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
         aspectratiotable[4], aspectratiotable[5], aspecttable[5],
         aspectratiotable[10], aspecttable[11]);
  printf("  (A tetrahedron's aspect ratio is its longest edge length");
  printf(" divided by its\n");
  printf("    smallest side height)\n\n");

  printf("  Face angle histogram:\n");
  for (i = 0; i < 9; i++) {
    printf("    %3d - %3d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           i * 10, i * 10 + 10, faceangletable[i],
           i * 10 + 90, i * 10 + 100, faceangletable[i + 9]);
  }
  if (minfaceang != PI) {
    printf("  Minimum input face angle is %g (degree).\n",
           minfaceang / PI * 180.0);
  }
  printf("\n");

  printf("  Dihedral angle histogram:\n");
  // Print the three two rows:
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         5, 10, dihedangletable[1], 110, 120, dihedangletable[10]);
  // Print the third to seventh rows.
  for (i = 2; i < 7; i++) {
    printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           (i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i],
           (i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]);
  }
  // Print the last two rows.
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
  printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         70, 80, dihedangletable[8], 175, 180, dihedangletable[17]);
  if (minfacetdihed != PI) {
    printf("  Minimum input facet dihedral angle is %g (degree).\n",
           minfacetdihed / PI * 180.0);
  }
  printf("\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// statistics()    Print all sorts of cool facts.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::statistics()
{
  printf("\nStatistics:\n\n");
  printf("  Input points: %d\n", in->numberofpoints + jettisoninverts);
  if (b->refine) {
    printf("  Input tetrahedra: %d\n", in->numberoftetrahedra);
  }
  if (b->plc) {
    printf("  Input facets: %d\n", in->numberoffacets);
    printf("  Input segments: %ld\n", insegments);
    printf("  Input holes: %d\n", in->numberofholes);
    printf("  Input regions: %d\n", in->numberofregions);
  }

  printf("\n  Mesh points: %ld\n", points->items);
  printf("  Mesh tetrahedra: %ld\n", tetrahedrons->items);
  if (b->plc || b->refine) {
    printf("  Mesh triangles: %ld\n", (4l*tetrahedrons->items+hullsize)/2l);
  }
  if (b->plc || b->refine) {
    printf("  Mesh subfaces: %ld\n", subfaces->items);
    printf("  Mesh subsegments: %ld\n\n", subsegs->items);
  } else {
    printf("  Convex hull triangles: %ld\n\n", hullsize);
  }
  if (b->verbose > 0) {
    qualitystatistics();
    unsigned long totalmeshbytes;
    printf("Memory allocation statistics:\n\n");
    printf("  Maximum number of vertices: %ld\n", points->maxitems);
    totalmeshbytes = points->maxitems * points->itembytes;
    printf("  Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems);
    totalmeshbytes += tetrahedrons->maxitems * tetrahedrons->itembytes;
    if (subfaces != (memorypool *) NULL) {
      printf("  Maximum number of subfaces: %ld\n", subfaces->maxitems);
      totalmeshbytes += subfaces->maxitems * subfaces->itembytes;
    }
    if (subsegs != (memorypool *) NULL) {
      printf("  Maximum number of segments: %ld\n", subsegs->maxitems);
      totalmeshbytes += subsegs->maxitems * subsegs->itembytes;
    }
    printf("  Approximate heap memory used by the mesh (K bytes): %g\n\n",
           (double) totalmeshbytes / 1024.0);
#ifdef SELF_CHECK
    algorithmicstatistics();
#endif
  }
}

//
// End of user interaction routines
//

//
// Begin of constructor and destructor of tetgenmesh
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// ~tetgenmesh()    Deallocte memory occupied by a tetgenmesh object.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::~tetgenmesh()
{
  bgm = (tetgenmesh *) NULL;
  in = (tetgenio *) NULL;
  b = (tetgenbehavior *) NULL;

  if (tetrahedrons != (memorypool *) NULL) {
    delete tetrahedrons;
  }
  if (subfaces != (memorypool *) NULL) {
    delete subfaces;
  }
  if (subsegs != (memorypool *) NULL) {
    delete subsegs;
  }
  if (points != (memorypool *) NULL) {
    delete points;
  }
  if (dummytetbase != (tetrahedron *) NULL) {
    delete [] dummytetbase;
  }
  if (dummyshbase != (shellface *) NULL) {
    delete [] dummyshbase;
  }
  if (facetabovepointarray != (point *) NULL) {
    delete [] facetabovepointarray;
  }
  if (highordertable != (point *) NULL) {
    delete [] highordertable;
  }
  if (subpbcgrouptable != (pbcdata *) NULL) {
    delete [] subpbcgrouptable;
  }
  if (segpbcgrouptable != (list *) NULL) {
    delete segpbcgrouptable;
    delete [] idx2segpglist;
    delete [] segpglist;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetgenmesh()    Initialize a tetgenmesh object.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::tetgenmesh()
{
  bgm = (tetgenmesh *) NULL;
  in = (tetgenio *) NULL;
  b = (tetgenbehavior *) NULL;

  tetrahedrons = (memorypool *) NULL;
  subfaces = (memorypool *) NULL;
  subsegs = (memorypool *) NULL;
  points = (memorypool *) NULL;
  badsubsegs = (memorypool *) NULL;
  badsubfaces = (memorypool *) NULL;
  badtetrahedrons = (memorypool *) NULL;
  flipstackers = (memorypool *) NULL;

  dummytet = (tetrahedron *) NULL;
  dummytetbase = (tetrahedron *) NULL;
  dummysh = (shellface *) NULL;
  dummyshbase = (shellface *) NULL;

  facetabovepointarray = (point *) NULL;
  abovepoint = (point) NULL;
  highordertable = (point *) NULL;
  subpbcgrouptable = (pbcdata *) NULL;
  segpbcgrouptable = (list *) NULL;
  idx2segpglist = (int *) NULL;
  segpglist = (int *) NULL;

  xmax = xmin = ymax = ymin = zmax = zmin = 0.0; 
  longest = 0.0;
  hullsize = 0l;
  insegments = 0l;
  pointmtrindex = 0;
  pointmarkindex = 0;
  point2simindex = 0;
  point2pbcptindex = 0;
  highorderindex = 0;
  elemattribindex = 0;
  volumeboundindex = 0;
  shmarkindex = 0;
  areaboundindex = 0;
  checksubfaces = 0;
  checksubsegs = 0;
  checkpbcs = 0;
  varconstraint = 0;
  nonconvex = 0;
  dupverts = 0;
  unuverts = 0;
  relverts = 0;
  suprelverts = 0;
  collapverts = 0;
  unsupverts = 0;
  jettisoninverts = 0;
  symbolic = 1;
  samples = 0l;
  randomseed = 1l;
  macheps = 0.0;
  minfaceang = minfacetdihed = PI;
  maxcavfaces = maxcavverts = 0;
  expcavcount = 0;
  abovecount = 0l;
  bowatvolcount = bowatsubcount = bowatsegcount = 0l;
  updvolcount = updsubcount = updsegcount = 0l;
  repairflipcount = 0l;
  outbowatcircumcount = 0l;
  failvolcount = failsubcount = failsegcount = 0l;
  r1count = r2count = r3count = 0l;
  cdtenforcesegpts = 0l;
  rejsegpts = rejsubpts = rejtetpts = 0l;
  flip23s = flip32s = flip22s = flip44s = 0l;
  tloctime = tfliptime = 0.0;
}

//
// End of constructor and destructor of tetgenmesh
//

//
// End of class 'tetgenmesh' implementation.
//

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// 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 PLC segments and facets (-p).                                //
// - 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). Also enforce the conforming Delaunay property (-q and -a). //
// - Promote the mesh's linear tetrahedra to higher order elements (-o).     //
// - Write the output files and print the statistics.                        //
// - Check the consistency and Delaunay property of the mesh (-C).           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out,
  tetgenio *addin, tetgenio *bgmin)
{
  tetgenmesh m;
  // Variables for timing the performance of TetGen (defined in time.h).
  clock_t tv[14];

  tv[0] = clock();
 
  m.b = b;
  m.in = in;
  m.macheps = exactinit();
  m.steinerleft = b->steiner;
  if (b->metric) {
    m.bgm = new tetgenmesh();
    m.bgm->b = b;
    m.bgm->in = bgmin;
    m.bgm->macheps = exactinit();
  }
  m.initializepools();
  m.transfernodes();

  tv[1] = clock();

  if (b->refine) {
    m.reconstructmesh();
  } else {
    m.delaunizevertices();
  }

  tv[2] = clock();

  if (!b->quiet) {
    if (b->refine) {
      printf("Mesh reconstruction seconds:");
    } else {
      printf("Delaunay seconds:");
    }
    printf("  %g\n", (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC);
  }

  if (b->metric) {
    if (bgmin != (tetgenio *) NULL) {
      m.bgm->initializepools();
      m.bgm->transfernodes();
      m.bgm->reconstructmesh();
    } else {
      m.bgm->in = in;
      m.bgm->initializepools();
      m.duplicatebgmesh();
    }
  }

  tv[3] = clock();

  if (!b->quiet) {
    if (b->metric) {
      printf("Background mesh reconstruct seconds:  %g\n",
             (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->useshelles && !b->refine) {
    m.meshsurface();
    if (b->diagnose != 1) {
      m.markacutevertices(89.0);
      m.incrperturbvertices(b->epsilon);
      m.delaunizesegments();
      if (m.checkpbcs) {
        long oldnum;
        do {
          oldnum = m.points->items;
          m.incrperturbvertices(b->epsilon);
          if (m.points->items > oldnum) {
            oldnum = m.points->items;
            m.delaunizesegments();
          }
        } while (oldnum < m.points->items);
      }
      m.constrainedfacets();
    } else {
      m.detectinterfaces();
    }
  }

  tv[4] = clock();

  if (!b->quiet) {
    if (b->useshelles && !b->refine) {
      if (b->diagnose != 1) {
        printf("Segment and facet ");
      } else {
        printf("Intersection "); 
      }
      printf("seconds:  %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->plc && !(b->diagnose == 1)) {
    m.carveholes();
  }

  tv[5] = clock();

  if (!b->quiet) {
    if (b->plc && !(b->diagnose == 1)) {
      printf("Hole seconds:  %g\n", (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if ((b->plc || b->refine) && !(b->diagnose == 1)) {
    m.optimizemesh(false); 
  }

  tv[6] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && !(b->diagnose == 1)) {
      printf("Repair seconds:  %g\n", (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if ((b->plc && b->nobisect) && !(b->diagnose == 1)) {
    m.removesteiners(false);
  }

  tv[7] = clock();

  if (!b->quiet) {
    if ((b->plc && b->nobisect) && !(b->diagnose == 1)) {
      printf("Steiner removal seconds:  %g\n",
             (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->insertaddpoints && (addin != (tetgenio *) NULL)) {
    if (addin->numberofpoints > 0) {
      m.insertconstrainedpoints(addin); 
    }
  }

  tv[8] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && (b->insertaddpoints)) {
      printf("Constrained points seconds:  %g\n", 
             (tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->metric) {
    m.interpolatesizemap();
  }

  tv[9] = clock();

  if (!b->quiet) {
    if (b->metric) {
      printf("Size interpolating seconds:  %g\n",
             (tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->coarse) {
    m.removesteiners(true);
  }

  tv[10] = clock();

  if (!b->quiet) {
    if (b->coarse) {
      printf("Mesh coarsening seconds:  %g\n",
             (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->quality) {
    m.enforcequality();
  }

  tv[11] = clock();

  if (!b->quiet) {
    if (b->quality) {
      printf("Quality seconds:  %g\n",
             (tv[11] - tv[10]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->quality && (b->optlevel > 0)) {
    m.optimizemesh(true);
  }

  tv[12] = clock();

  if (!b->quiet) {
    if (b->quality && (b->optlevel > 0)) {
      printf("Optimize seconds:  %g\n",
             (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0)
      || (b->refine && (in->numberofcorners == 10)))) {
    m.jettisonnodes();
  }

  if (b->order > 1) {
    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 {
    if (b->diagnose == 1) {
      if (m.subfaces->items > 0l) {
        m.outnodes(out);  // Only output when self-intersecting faces exist.
      }
    } else {
      m.outnodes(out);
      if (b->quality || b->metric) {
        // m.outmetrics(out);
      }
    }
  }

  if (b->noelewritten) {
    if (!b->quiet) {
      printf("NOT writing an .ele file.\n");
    }
  } else {
    if (!(b->diagnose == 1)) {
      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->diagnose == 1) {
        if (m.subfaces->items > 0l) {
          m.outsubfaces(out); // Only output self-intersecting 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 (m.checkpbcs) {
    m.outpbcnodes(out);
  }

  if (b->edgesout) {
    if (b->edgesout > 1) {
      m.outedges(out); // -ee, output all mesh edges. 
    } else {
      m.outsubsegments(out); // -e, only output subsegments.
    }
  }

  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->gidview) {
    m.outmesh2gid(b->outfilename); 
  }

  if (!out && b->geomview) {
    m.outmesh2off(b->outfilename); 
  }

  if (b->neighout) {
    m.outneighbors(out);
  }

  if (b->voroout) {
    m.outvoronoi(out);
  }

  tv[13] = clock();

  if (!b->quiet) {
    printf("\nOutput seconds:  %g\n",
           (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC);
    printf("Total running seconds:  %g\n",
           (tv[13] - tv[0]) / (REAL) CLOCKS_PER_SEC);
  }

  if (b->docheck) {
    m.checkmesh();
    if (m.checksubfaces) {
      m.checkshells();
    }
    if (b->docheck > 1) {
      m.checkdelaunay(0.0, NULL);
      if (b->docheck > 2) {
        if (b->quality || b->refine) {
          m.checkconforming();
        }
      }
    }
  }

  if (!b->quiet) {
    m.statistics();
  }

  if (b->metric) {
    delete m.bgm;
  }
}

#ifndef TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// main()    The entrance for running TetGen from command line.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])

#else // with TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedralize()    The entrance for calling TetGen from another program. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetrahedralize(char *switches, tetgenio *in, tetgenio *out,
  tetgenio *addin, tetgenio *bgmin)

#endif // not TETLIBRARY

{
  tetgenbehavior b;

#ifndef TETLIBRARY

  tetgenio in, addin, bgmin;
  
  if (!b.parse_commandline(argc, argv)) {
    terminatetetgen(1);
  }
  if (b.refine) {
    if (!in.load_tetmesh(b.infilename)) {
      terminatetetgen(1);
    }
  } else {
    if (!in.load_plc(b.infilename, (int) b.object)) {
      terminatetetgen(1);
    }
  }
  if (b.insertaddpoints) {
    if (!addin.load_node(b.addinfilename)) {
      addin.numberofpoints = 0l;
    }
  }
  if (b.metric) {
    if (!bgmin.load_tetmesh(b.bgmeshfilename)) {
      bgmin.numberoftetrahedra = 0l;
    }
  }

  if (bgmin.numberoftetrahedra > 0l) {
    tetrahedralize(&b, &in, NULL, &addin, &bgmin);
  } else {
    tetrahedralize(&b, &in, NULL, &addin, NULL);
  }

  return 0;

#else // with TETLIBRARY

  if (!b.parse_commandline(switches)) {
    terminatetetgen(1);
  }
  tetrahedralize(&b, in, out, addin, bgmin);

#endif // not TETLIBRARY
}

